summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAndrej Mihajlov <and@mullvad.net>2022-09-29 15:28:07 +0200
committerAndrej Mihajlov <and@mullvad.net>2022-09-29 15:28:07 +0200
commit06f7d719b85d84f5231359f04867c5cee6d66029 (patch)
tree3048b9120a2527f889339b53914d592a61060001
parent724faeaafa097e0cd4e0157a7aa6ae205e43699a (diff)
parent88fc18a39e960f1b0e16b3b4f2c0e7d76e7ebf01 (diff)
downloadmullvadvpn-06f7d719b85d84f5231359f04867c5cee6d66029.tar.xz
mullvadvpn-06f7d719b85d84f5231359f04867c5cee6d66029.zip
Merge branch 'device-management-view-issue-on-smaller-iphones'
-rw-r--r--ios/MullvadVPN/AppDelegate.swift6
-rw-r--r--ios/MullvadVPN/AutomaticKeyboardResponder.swift4
-rw-r--r--ios/MullvadVPN/DataSourceSnapshot.swift12
-rw-r--r--ios/MullvadVPN/DeviceManagementContentView.swift174
-rw-r--r--ios/MullvadVPN/DeviceManagementViewController.swift23
-rw-r--r--ios/MullvadVPN/NotificationController.swift7
-rw-r--r--ios/MullvadVPN/SelectLocationViewController.swift4
-rw-r--r--ios/MullvadVPN/TunnelManager/PacketTunnelOptions.swift9
-rw-r--r--ios/MullvadVPNScreenshots/MullvadVPNScreenshots.swift9
-rw-r--r--ios/Operations/AlertPresenter.swift3
10 files changed, 142 insertions, 109 deletions
diff --git a/ios/MullvadVPN/AppDelegate.swift b/ios/MullvadVPN/AppDelegate.swift
index e08d84f025..92c99794fa 100644
--- a/ios/MullvadVPN/AppDelegate.swift
+++ b/ios/MullvadVPN/AppDelegate.swift
@@ -104,8 +104,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
didDiscardSceneSessions sceneSessions: Set<UISceneSession>
) {
// Called when the user discards a scene session.
- // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
- // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
+ // If any sessions were discarded while the application was not running,
+ // this will be called shortly after application:didFinishLaunchingWithOptions.
+ // Use this method to release any resources that were specific to
+ // the discarded scenes, as they will not return.
}
// MARK: - Background tasks
diff --git a/ios/MullvadVPN/AutomaticKeyboardResponder.swift b/ios/MullvadVPN/AutomaticKeyboardResponder.swift
index 553b9d948a..d10216b352 100644
--- a/ios/MullvadVPN/AutomaticKeyboardResponder.swift
+++ b/ios/MullvadVPN/AutomaticKeyboardResponder.swift
@@ -141,8 +141,8 @@ class AutomaticKeyboardResponder {
// Determine presentation style within the context
let presentationStyle: UIModalPresentationStyle
- // Use the presentation style of a presented controller when parent controller is being presented as a child of
- // other modal controller.
+ // Use the presentation style of a presented controller,
+ // when parent controller is being presented as a child of other modal controller.
if let presented = parent.presentingViewController?.presentedViewController {
presentationStyle = presented.modalPresentationStyle
} else {
diff --git a/ios/MullvadVPN/DataSourceSnapshot.swift b/ios/MullvadVPN/DataSourceSnapshot.swift
index 6e19708776..b68935000c 100644
--- a/ios/MullvadVPN/DataSourceSnapshot.swift
+++ b/ios/MullvadVPN/DataSourceSnapshot.swift
@@ -281,8 +281,8 @@ extension DataSourceSnapshot {
return Self.changeSetToDifference(changes)
}
- /// Infer and discard unnecessary moves that occur due to items shifting back or forth based on insertions and
- /// deletions of other items.
+ /// Infer and discard unnecessary moves that occur due to items shifting back or forth based on
+ /// insertions and deletions of other items.
private static func inferMoves(changes: [Change]) -> [Change] {
var newChanges = [Change]()
@@ -299,8 +299,8 @@ extension DataSourceSnapshot {
continue
}
- // Replay all changes to compute the item's index path, ignoring the changes associated with the current
- // change.
+ // Replay all changes to compute the item's index path, ignoring the changes
+ // associated with the current change.
let inferredIndexPath = sortedChangesWithoutMoves
.reduce(into: sourceIndexPath) { inferredIndexPath, otherChange in
switch otherChange {
@@ -323,8 +323,8 @@ extension DataSourceSnapshot {
}
}
- // Discard the change if the index path, produced after replaying other changes, matches the target index
- // path.
+ // Discard the change if the index path, produced after replaying other changes,
+ // matches the target index path.
if inferredIndexPath != targetIndexPath {
newChanges.append(contentsOf: sourceChange.breakMoveOntoInsertionDeletion())
}
diff --git a/ios/MullvadVPN/DeviceManagementContentView.swift b/ios/MullvadVPN/DeviceManagementContentView.swift
index abff4306b2..141ec9e007 100644
--- a/ios/MullvadVPN/DeviceManagementContentView.swift
+++ b/ios/MullvadVPN/DeviceManagementContentView.swift
@@ -9,6 +9,19 @@
import UIKit
class DeviceManagementContentView: UIView {
+ private let scrollView: UIScrollView = {
+ let scrollView = UIScrollView()
+ scrollView.translatesAutoresizingMaskIntoConstraints = false
+ return scrollView
+ }()
+
+ let scrollContentView: UIView = {
+ let view = UIView()
+ view.layoutMargins = UIMetrics.contentLayoutMargins
+ view.translatesAutoresizingMaskIntoConstraints = false
+ return view
+ }()
+
let statusImageView: StatusImageView = {
let imageView = StatusImageView(style: .failure)
imageView.translatesAutoresizingMaskIntoConstraints = false
@@ -36,6 +49,16 @@ class DeviceManagementContentView: UIView {
return textLabel
}()
+ let deviceStackView: UIStackView = {
+ let stackView = UIStackView(arrangedSubviews: [])
+ stackView.translatesAutoresizingMaskIntoConstraints = false
+ stackView.axis = .vertical
+ stackView.spacing = 1
+ stackView.clipsToBounds = true
+ stackView.distribution = .fillEqually
+ return stackView
+ }()
+
let continueButton: AppButton = {
let button = AppButton(style: .success)
button.translatesAutoresizingMaskIntoConstraints = false
@@ -48,6 +71,7 @@ class DeviceManagementContentView: UIView {
),
for: .normal
)
+ button.isEnabled = false
return button
}()
@@ -66,32 +90,100 @@ class DeviceManagementContentView: UIView {
return button
}()
- let deviceStackView: UIStackView = {
- let stackView = UIStackView(arrangedSubviews: [])
- stackView.translatesAutoresizingMaskIntoConstraints = false
- stackView.axis = .vertical
- stackView.spacing = 1
- stackView.clipsToBounds = true
- return stackView
- }()
-
- lazy var buttonStackView: UIStackView = {
+ private lazy var buttonStackView: UIStackView = {
let stackView = UIStackView(arrangedSubviews: [continueButton, backButton])
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .vertical
+ stackView.distribution = .fillEqually
stackView.spacing = UIMetrics.interButtonSpacing
return stackView
}()
+ var handleDeviceDeletion: ((DeviceViewModel, @escaping () -> Void) -> Void)?
+
+ private var currentSnapshot = DataSourceSnapshot<String, String>()
+
var canContinue = false {
didSet {
updateView()
}
}
- var handleDeviceDeletion: ((DeviceViewModel, @escaping () -> Void) -> Void)?
+ override init(frame: CGRect) {
+ super.init(frame: frame)
- private var currentSnapshot = DataSourceSnapshot<String, String>()
+ addViews()
+ constraintViews()
+ updateView()
+ }
+
+ private func addViews() {
+ [scrollView, buttonStackView].forEach(addSubview)
+
+ scrollView.addSubview(scrollContentView)
+
+ [statusImageView, titleLabel, messageLabel, deviceStackView]
+ .forEach(scrollContentView.addSubview)
+ }
+
+ private func constraintViews() {
+ NSLayoutConstraint.activate([
+ scrollView.topAnchor.constraint(equalTo: topAnchor),
+ scrollView.leadingAnchor.constraint(equalTo: leadingAnchor),
+ scrollView.trailingAnchor.constraint(equalTo: trailingAnchor),
+
+ buttonStackView.topAnchor.constraint(
+ equalTo: scrollView.bottomAnchor,
+ constant: UIMetrics.contentLayoutMargins.top
+ ),
+ buttonStackView.leadingAnchor.constraint(
+ equalTo: leadingAnchor,
+ constant: UIMetrics.contentLayoutMargins.left
+ ),
+ buttonStackView.trailingAnchor.constraint(
+ equalTo: trailingAnchor,
+ constant: -UIMetrics.contentLayoutMargins.right
+ ),
+ buttonStackView.bottomAnchor.constraint(
+ equalTo: safeAreaLayoutGuide.bottomAnchor,
+ constant: -UIMetrics.contentLayoutMargins.bottom
+ ),
+
+ scrollContentView.topAnchor.constraint(equalTo: scrollView.topAnchor),
+ scrollContentView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
+ scrollContentView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
+ scrollContentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
+ scrollContentView.widthAnchor.constraint(equalTo: scrollView.widthAnchor),
+
+ statusImageView.topAnchor
+ .constraint(equalTo: scrollContentView.layoutMarginsGuide.topAnchor),
+ statusImageView.centerXAnchor.constraint(equalTo: scrollContentView.centerXAnchor),
+
+ titleLabel.topAnchor.constraint(equalTo: statusImageView.bottomAnchor, constant: 22),
+ titleLabel.leadingAnchor
+ .constraint(equalTo: scrollContentView.layoutMarginsGuide.leadingAnchor),
+ titleLabel.trailingAnchor
+ .constraint(equalTo: scrollContentView.layoutMarginsGuide.trailingAnchor),
+
+ messageLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 8),
+ messageLabel.leadingAnchor
+ .constraint(equalTo: scrollContentView.layoutMarginsGuide.leadingAnchor),
+ messageLabel.trailingAnchor
+ .constraint(equalTo: scrollContentView.layoutMarginsGuide.trailingAnchor),
+
+ deviceStackView.topAnchor.constraint(
+ equalTo: messageLabel.bottomAnchor,
+ constant: UIMetrics.sectionSpacing
+ ),
+ deviceStackView.leadingAnchor.constraint(equalTo: scrollContentView.leadingAnchor),
+ deviceStackView.trailingAnchor.constraint(equalTo: scrollContentView.trailingAnchor),
+ deviceStackView.bottomAnchor.constraint(equalTo: scrollContentView.bottomAnchor),
+ ])
+ }
+
+ required init?(coder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
func setDeviceViewModels(_ newModels: [DeviceViewModel], animated: Bool) {
var newSnapshot = DataSourceSnapshot<String, String>()
@@ -118,69 +210,15 @@ class DeviceManagementContentView: UIView {
diff.apply(
to: deviceStackView,
configuration: applyConfiguration,
- animateDifferences: true
+ animateDifferences: animated
)
}
- override init(frame: CGRect) {
- super.init(frame: frame)
-
- layoutMargins = UIMetrics.contentLayoutMargins
-
- let spacer = UIView()
- spacer.translatesAutoresizingMaskIntoConstraints = false
- spacer.setContentHuggingPriority(.defaultLow - 1, for: .vertical)
- spacer.setContentCompressionResistancePriority(.defaultLow, for: .vertical)
-
- let subviewsToAdd = [
- statusImageView, titleLabel, messageLabel, deviceStackView, spacer, buttonStackView,
- ]
- for subview in subviewsToAdd {
- addSubview(subview)
- }
-
- updateView()
-
- NSLayoutConstraint.activate([
- statusImageView.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor),
- statusImageView.centerXAnchor.constraint(equalTo: centerXAnchor),
-
- titleLabel.topAnchor.constraint(equalTo: statusImageView.bottomAnchor, constant: 22),
- titleLabel.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor),
- titleLabel.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor),
-
- messageLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 8),
- messageLabel.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor),
- messageLabel.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor),
-
- deviceStackView.topAnchor.constraint(
- equalTo: messageLabel.bottomAnchor,
- constant: UIMetrics.sectionSpacing
- ),
- deviceStackView.leadingAnchor.constraint(equalTo: leadingAnchor),
- deviceStackView.trailingAnchor.constraint(equalTo: trailingAnchor),
-
- spacer.topAnchor.constraint(equalTo: deviceStackView.bottomAnchor),
- spacer.leadingAnchor.constraint(equalTo: deviceStackView.leadingAnchor),
- spacer.trailingAnchor.constraint(equalTo: deviceStackView.trailingAnchor),
- spacer.heightAnchor.constraint(greaterThanOrEqualToConstant: UIMetrics.sectionSpacing),
-
- buttonStackView.topAnchor.constraint(equalTo: spacer.bottomAnchor),
- buttonStackView.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor),
- buttonStackView.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor),
- buttonStackView.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor),
- ])
- }
-
- required init?(coder: NSCoder) {
- fatalError("init(coder:) has not been implemented")
- }
-
private func updateView() {
titleLabel.text = titleText
messageLabel.text = messageText
- statusImageView.style = canContinue ? .success : .failure
continueButton.isEnabled = canContinue
+ statusImageView.style = canContinue ? .success : .failure
}
private var titleText: String {
diff --git a/ios/MullvadVPN/DeviceManagementViewController.swift b/ios/MullvadVPN/DeviceManagementViewController.swift
index 3a2915fae4..2b9f588c9d 100644
--- a/ios/MullvadVPN/DeviceManagementViewController.swift
+++ b/ios/MullvadVPN/DeviceManagementViewController.swift
@@ -56,10 +56,7 @@ class DeviceManagementViewController: UIViewController, RootContainment {
view.backgroundColor = .secondaryColor
- let scrollView = UIScrollView()
- scrollView.translatesAutoresizingMaskIntoConstraints = false
- scrollView.addSubview(contentView)
- view.addSubview(scrollView)
+ view.addSubview(contentView)
contentView.backButton.addTarget(
self,
@@ -78,20 +75,10 @@ class DeviceManagementViewController: UIViewController, RootContainment {
}
NSLayoutConstraint.activate([
- scrollView.topAnchor.constraint(equalTo: view.topAnchor),
- scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
- scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
- scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
-
- contentView.topAnchor.constraint(equalTo: scrollView.topAnchor),
- contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
- contentView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
- contentView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
- contentView.widthAnchor.constraint(equalTo: scrollView.widthAnchor),
-
- contentView.heightAnchor.constraint(
- greaterThanOrEqualTo: scrollView.frameLayoutGuide.heightAnchor
- ),
+ contentView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
+ contentView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
+ contentView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
+ contentView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
])
}
diff --git a/ios/MullvadVPN/NotificationController.swift b/ios/MullvadVPN/NotificationController.swift
index 9585c26ff9..8d07dfefb9 100644
--- a/ios/MullvadVPN/NotificationController.swift
+++ b/ios/MullvadVPN/NotificationController.swift
@@ -65,8 +65,8 @@ class NotificationController: UIViewController {
showsBanner = show
if show {
- // Make sure to lay out the banner before animating its appearance to avoid undesired horizontal expansion
- // animation.
+ // Make sure to lay out the banner before animating its appearance to
+ // avoid undesired horizontal expansion animation.
view.layoutIfNeeded()
bannerView.isHidden = false
@@ -125,7 +125,8 @@ class NotificationController: UIViewController {
animator.startAnimation()
}
- // Do not emit the .layoutChanged unless the banner is focused to avoid capturing the voice over focus.
+ // Do not emit the .layoutChanged unless the banner is focused to avoid capturing
+ // the voice over focus.
if bannerView.accessibilityElementIsFocused() {
UIAccessibility.post(notification: .layoutChanged, argument: bannerView)
}
diff --git a/ios/MullvadVPN/SelectLocationViewController.swift b/ios/MullvadVPN/SelectLocationViewController.swift
index b9d52107fa..5f7652477c 100644
--- a/ios/MullvadVPN/SelectLocationViewController.swift
+++ b/ios/MullvadVPN/SelectLocationViewController.swift
@@ -272,8 +272,8 @@ class SelectLocationViewController: UIViewController, UITableViewDelegate {
// MARK: - Private
private func updateTableHeaderTopLayoutMargin() {
- // When contained within the navigation controller, we want the distance between the navigation title
- // and the table header label to be exactly 24pt.
+ // When contained within the navigation controller, we want the distance between
+ // the navigation title and the table header label to be exactly 24pt.
if let navigationBar = navigationController?.navigationBar as? CustomNavigationBar,
!showHeaderViewAtTheBottom
{
diff --git a/ios/MullvadVPN/TunnelManager/PacketTunnelOptions.swift b/ios/MullvadVPN/TunnelManager/PacketTunnelOptions.swift
index d6a3578bc5..850c24f37c 100644
--- a/ios/MullvadVPN/TunnelManager/PacketTunnelOptions.swift
+++ b/ios/MullvadVPN/TunnelManager/PacketTunnelOptions.swift
@@ -11,12 +11,13 @@ import Foundation
struct PacketTunnelOptions {
/// Keys for options dictionary
private enum Keys: String {
- /// Option key that holds the `NSData` value with `RelaySelectorResult` encoded using `JSONEncoder`.
- /// Used for passing the pre-selected relay in the GUI proocess to the Packet tunnel process.
+ /// Option key that holds the `NSData` value with `RelaySelectorResult`
+ /// encoded using `JSONEncoder`.
+ /// Used for passing the pre-selected relay in the GUI process to the Packet tunnel process.
case relaySelectorResult = "relay-selector-result"
- /// Option key that holds the `NSNumber` value, which is when set to `1` indicates that the tunnel was started by
- /// the system.
+ /// Option key that holds the `NSNumber` value, which is when set to `1` indicates that
+ /// the tunnel was started by the system.
/// System automatically provides that flag to the tunnel.
case isOnDemand = "is-on-demand"
}
diff --git a/ios/MullvadVPNScreenshots/MullvadVPNScreenshots.swift b/ios/MullvadVPNScreenshots/MullvadVPNScreenshots.swift
index 7cf720c959..ea72413456 100644
--- a/ios/MullvadVPNScreenshots/MullvadVPNScreenshots.swift
+++ b/ios/MullvadVPNScreenshots/MullvadVPNScreenshots.swift
@@ -10,16 +10,19 @@ import XCTest
class MullvadVPNScreenshots: XCTestCase {
override func setUp() {
- // Put setup code here. This method is called before the invocation of each test method in the class.
+ // Put setup code here. This method is called before the invocation of
+ // each test method in the class.
// In UI tests it is usually best to stop immediately when a failure occurs.
continueAfterFailure = false
- // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
+ // In UI tests it’s important to set the initial state - such as interface orientation -
+ // required for your tests before they run. The setUp method is a good place to do this.
}
override func tearDown() {
- // Put teardown code here. This method is called after the invocation of each test method in the class.
+ // Put teardown code here. This method is called after the invocation of
+ // each test method in the class.
}
func testTakeScreenshots() {
diff --git a/ios/Operations/AlertPresenter.swift b/ios/Operations/AlertPresenter.swift
index 6bf4470e23..1af6a9435d 100644
--- a/ios/Operations/AlertPresenter.swift
+++ b/ios/Operations/AlertPresenter.swift
@@ -24,7 +24,8 @@ public final class AlertPresenter {
private static let initClass: Void = {
/// Swizzle `viewDidDisappear` on `UIAlertController` in order to be able to
/// detect when the controller disappears.
- /// The event is broadcasted via `AlertPresenter.alertControllerDidDismissNotification` notification.
+ /// The event is broadcasted via
+ /// `AlertPresenter.alertControllerDidDismissNotification` notification.
swizzleMethod(
aClass: UIAlertController.self,
originalSelector: #selector(UIAlertController.viewDidDisappear(_:)),