diff options
| author | Andrej Mihajlov <and@mullvad.net> | 2022-09-29 15:28:07 +0200 |
|---|---|---|
| committer | Andrej Mihajlov <and@mullvad.net> | 2022-09-29 15:28:07 +0200 |
| commit | 06f7d719b85d84f5231359f04867c5cee6d66029 (patch) | |
| tree | 3048b9120a2527f889339b53914d592a61060001 | |
| parent | 724faeaafa097e0cd4e0157a7aa6ae205e43699a (diff) | |
| parent | 88fc18a39e960f1b0e16b3b4f2c0e7d76e7ebf01 (diff) | |
| download | mullvadvpn-06f7d719b85d84f5231359f04867c5cee6d66029.tar.xz mullvadvpn-06f7d719b85d84f5231359f04867c5cee6d66029.zip | |
Merge branch 'device-management-view-issue-on-smaller-iphones'
| -rw-r--r-- | ios/MullvadVPN/AppDelegate.swift | 6 | ||||
| -rw-r--r-- | ios/MullvadVPN/AutomaticKeyboardResponder.swift | 4 | ||||
| -rw-r--r-- | ios/MullvadVPN/DataSourceSnapshot.swift | 12 | ||||
| -rw-r--r-- | ios/MullvadVPN/DeviceManagementContentView.swift | 174 | ||||
| -rw-r--r-- | ios/MullvadVPN/DeviceManagementViewController.swift | 23 | ||||
| -rw-r--r-- | ios/MullvadVPN/NotificationController.swift | 7 | ||||
| -rw-r--r-- | ios/MullvadVPN/SelectLocationViewController.swift | 4 | ||||
| -rw-r--r-- | ios/MullvadVPN/TunnelManager/PacketTunnelOptions.swift | 9 | ||||
| -rw-r--r-- | ios/MullvadVPNScreenshots/MullvadVPNScreenshots.swift | 9 | ||||
| -rw-r--r-- | ios/Operations/AlertPresenter.swift | 3 |
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(_:)), |
