summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--ios/MullvadVPN.xcodeproj/project.pbxproj8
-rw-r--r--ios/MullvadVPN/ConnectMainContentView.swift147
-rw-r--r--ios/MullvadVPN/ConnectViewController.swift143
-rw-r--r--ios/MullvadVPN/ConnectViewController.xib113
-rw-r--r--ios/MullvadVPN/DisconnectSplitButton.swift30
-rw-r--r--ios/MullvadVPN/RootContainerViewController.swift4
6 files changed, 235 insertions, 210 deletions
diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj
index 9e5371459d..394992b99b 100644
--- a/ios/MullvadVPN.xcodeproj/project.pbxproj
+++ b/ios/MullvadVPN.xcodeproj/project.pbxproj
@@ -154,6 +154,7 @@
58B0A2AC238EE6D500BC001D /* IPAddress+Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5840250022B1124600E4CFEC /* IPAddress+Codable.swift */; };
58B0A2AD238EE6EC00BC001D /* MullvadEndpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5840250322B11AB700E4CFEC /* MullvadEndpoint.swift */; };
58B67B482602079E008EF58E /* RelaySelector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58781CD422AFBA39009B9D8E /* RelaySelector.swift */; };
+ 58B43C1925F77DB60002C8C3 /* ConnectMainContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58B43C1825F77DB60002C8C3 /* ConnectMainContentView.swift */; };
58B8743222B25A7600015324 /* WireguardAssociatedAddresses.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58B8743122B25A7600015324 /* WireguardAssociatedAddresses.swift */; };
58B8743B22B788D200015324 /* PacketTunnelSettingsGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58B8743722B25EAB00015324 /* PacketTunnelSettingsGenerator.swift */; };
58B9814E24FEA70D00C0D59E /* WireguardKeysViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 58B9814D24FEA70D00C0D59E /* WireguardKeysViewController.xib */; };
@@ -193,7 +194,6 @@
58CE5E81224146470008646E /* PacketTunnel.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 58CE5E79224146470008646E /* PacketTunnel.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
58D0C79E23F1CEBA00FE9BA7 /* SnapshotHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58D0C79D23F1CEBA00FE9BA7 /* SnapshotHelper.swift */; };
58D0C7A223F1CECF00FE9BA7 /* MullvadVPNScreenshots.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58D0C7A023F1CECF00FE9BA7 /* MullvadVPNScreenshots.swift */; };
- 58D9AF6B2501111800B6FAB5 /* ConnectViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 58D9AF6A2501111800B6FAB5 /* ConnectViewController.xib */; };
58DF28A52417CB4B00E836B0 /* AppStorePaymentManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58DF28A42417CB4B00E836B0 /* AppStorePaymentManager.swift */; };
58E5BC2624FEB6DB00A53A76 /* AccountViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 58E5BC2524FEB6DB00A53A76 /* AccountViewController.xib */; };
58E6771F24ADFE7800AA26E7 /* SettingsNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58E6771E24ADFE7800AA26E7 /* SettingsNavigationController.swift */; };
@@ -357,6 +357,7 @@
58AEEF6A2344A46200C9BBD5 /* TunnelSettingsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelSettingsManager.swift; sourceTree = "<group>"; };
58B0A2A0238EE67E00BC001D /* MullvadVPNTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MullvadVPNTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
58B0A2A4238EE67E00BC001D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+ 58B43C1825F77DB60002C8C3 /* ConnectMainContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectMainContentView.swift; sourceTree = "<group>"; };
58B8743122B25A7600015324 /* WireguardAssociatedAddresses.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WireguardAssociatedAddresses.swift; sourceTree = "<group>"; };
58B8743722B25EAB00015324 /* PacketTunnelSettingsGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PacketTunnelSettingsGenerator.swift; sourceTree = "<group>"; };
58B9814D24FEA70D00C0D59E /* WireguardKeysViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = WireguardKeysViewController.xib; sourceTree = "<group>"; };
@@ -393,7 +394,6 @@
58D0C79D23F1CEBA00FE9BA7 /* SnapshotHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SnapshotHelper.swift; sourceTree = "<group>"; };
58D0C79F23F1CECF00FE9BA7 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
58D0C7A023F1CECF00FE9BA7 /* MullvadVPNScreenshots.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MullvadVPNScreenshots.swift; sourceTree = "<group>"; };
- 58D9AF6A2501111800B6FAB5 /* ConnectViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ConnectViewController.xib; sourceTree = "<group>"; };
58DF28A42417CB4B00E836B0 /* AppStorePaymentManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppStorePaymentManager.swift; sourceTree = "<group>"; };
58E5BC2524FEB6DB00A53A76 /* AccountViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AccountViewController.xib; sourceTree = "<group>"; };
58E6771E24ADFE7800AA26E7 /* SettingsNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsNavigationController.swift; sourceTree = "<group>"; };
@@ -564,7 +564,7 @@
58F840B12464491D0044E708 /* ChainedError.swift */,
58A1AA8B23F5584B009F7EA6 /* ConnectionPanelView.swift */,
58CCA00F224249A1004F3011 /* ConnectViewController.swift */,
- 58D9AF6A2501111800B6FAB5 /* ConnectViewController.xib */,
+ 58B43C1825F77DB60002C8C3 /* ConnectMainContentView.swift */,
58A99ED2240014A0006599E9 /* ConsentViewController.swift */,
58AB9DEB2501040C006C5526 /* ConsentViewController.xib */,
5896AE83246D5889005B36CB /* CustomDateComponentsFormatting.swift */,
@@ -873,7 +873,6 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- 58D9AF6B2501111800B6FAB5 /* ConnectViewController.xib in Resources */,
58AB9DEE25010636006C5526 /* LoginViewController.xib in Resources */,
58F3C0A624A50157003E76BE /* relays.json in Resources */,
58CE5E6E224146210008646E /* LaunchScreen.storyboard in Resources */,
@@ -1051,6 +1050,7 @@
581503A324D6F1EC00C9C50E /* ChainedError+Logger.swift in Sources */,
58FD5BF024238EB300112C88 /* SKProduct+Formatting.swift in Sources */,
58C3B06724EA768100C0348E /* LogStreamerViewController.swift in Sources */,
+ 58B43C1925F77DB60002C8C3 /* ConnectMainContentView.swift in Sources */,
58561C99239A5D1500BD6B5E /* IPEndpoint.swift in Sources */,
58FD5BF22424F7D700112C88 /* UserInterfaceInteractionRestriction.swift in Sources */,
5811DE50239014550011EB53 /* NEVPNStatus+Debug.swift in Sources */,
diff --git a/ios/MullvadVPN/ConnectMainContentView.swift b/ios/MullvadVPN/ConnectMainContentView.swift
new file mode 100644
index 0000000000..95e97128e3
--- /dev/null
+++ b/ios/MullvadVPN/ConnectMainContentView.swift
@@ -0,0 +1,147 @@
+//
+// ConnectMainContentView.swift
+// MullvadVPN
+//
+// Created by pronebird on 09/03/2021.
+// Copyright © 2021 Mullvad VPN AB. All rights reserved.
+//
+
+import UIKit
+
+class ConnectMainContentView: UIView {
+ enum ActionButton {
+ case connect
+ case disconnect
+ case selectLocation
+ }
+
+ let secureLabel = makeBoldTextLabel(ofSize: 20)
+ let countryLabel = makeBoldTextLabel(ofSize: 34)
+ let cityLabel = makeBoldTextLabel(ofSize: 34)
+
+ lazy var connectionPanel: ConnectionPanelView = {
+ let view = ConnectionPanelView()
+ view.translatesAutoresizingMaskIntoConstraints = false
+ return view
+ }()
+
+ lazy var buttonsStackView: UIStackView = {
+ let stackView = UIStackView()
+ stackView.spacing = 16
+ stackView.axis = .vertical
+ stackView.translatesAutoresizingMaskIntoConstraints = false
+ return stackView
+ }()
+
+ lazy var connectButton: AppButton = {
+ let button = AppButton(style: .success)
+ button.translatesAutoresizingMaskIntoConstraints = false
+ button.titleLabel?.font = UIFont.systemFont(ofSize: 18, weight: .semibold)
+ return button
+ }()
+
+ lazy var selectLocationButton: AppButton = {
+ let button = AppButton(style: .translucentNeutral)
+ button.accessibilityIdentifier = "SelectLocationButton"
+ button.translatesAutoresizingMaskIntoConstraints = false
+ button.titleLabel?.font = UIFont.systemFont(ofSize: 18, weight: .semibold)
+ return button
+ }()
+
+ let splitDisconnectButton: DisconnectSplitButton = {
+ let button = DisconnectSplitButton()
+ button.primaryButton.accessibilityIdentifier = "DisconnectButton"
+ button.translatesAutoresizingMaskIntoConstraints = false
+ return button
+ }()
+
+ let containerView: UIView = {
+ let view = UIView()
+ view.translatesAutoresizingMaskIntoConstraints = false
+ return view
+ }()
+
+ override init(frame: CGRect) {
+ super.init(frame: frame)
+
+ backgroundColor = .primaryColor
+ layoutMargins = UIEdgeInsets(top: 24, left: 24, bottom: 24, right: 24)
+
+ addSubviews()
+ }
+
+ required init?(coder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+ func setActionButtons(_ actionButtons: [ActionButton]) {
+ let views = actionButtons.map { self.view(forActionButton: $0) }
+
+ setArrangedButtons(views)
+ }
+
+ private class func makeBoldTextLabel(ofSize fontSize: CGFloat) -> UILabel {
+ let textLabel = UILabel()
+ textLabel.translatesAutoresizingMaskIntoConstraints = false
+ textLabel.font = UIFont.boldSystemFont(ofSize: fontSize)
+ textLabel.textColor = .white
+ return textLabel
+ }
+
+ private func addSubviews() {
+ addSubview(containerView)
+ [secureLabel, countryLabel, cityLabel, connectionPanel, buttonsStackView].forEach { containerView.addSubview($0) }
+
+ NSLayoutConstraint.activate([
+ containerView.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor),
+ containerView.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor),
+ containerView.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor),
+ containerView.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor),
+
+ secureLabel.topAnchor.constraint(greaterThanOrEqualTo: containerView.topAnchor),
+ secureLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
+ secureLabel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
+
+ countryLabel.topAnchor.constraint(equalTo: secureLabel.bottomAnchor, constant: 8),
+ countryLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
+ countryLabel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
+
+ cityLabel.topAnchor.constraint(equalTo: countryLabel.bottomAnchor, constant: 8),
+ cityLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
+ cityLabel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
+
+ connectionPanel.topAnchor.constraint(equalTo: cityLabel.bottomAnchor, constant: 8),
+ connectionPanel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
+ connectionPanel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
+
+ buttonsStackView.topAnchor.constraint(equalTo: connectionPanel.bottomAnchor, constant: 24),
+ buttonsStackView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
+ buttonsStackView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
+ buttonsStackView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor)
+ ])
+ }
+
+ private func setArrangedButtons(_ newButtons: [UIView]) {
+ buttonsStackView.arrangedSubviews.forEach { (button) in
+ if !newButtons.contains(button) {
+ buttonsStackView.removeArrangedSubview(button)
+ button.removeFromSuperview()
+ }
+ }
+
+ newButtons.forEach { (button) in
+ buttonsStackView.addArrangedSubview(button)
+ }
+ }
+
+ private func view(forActionButton actionButton: ActionButton) -> UIView {
+ switch actionButton {
+ case .connect:
+ return connectButton
+ case .disconnect:
+ return splitDisconnectButton
+ case .selectLocation:
+ return selectLocationButton
+ }
+ }
+}
diff --git a/ios/MullvadVPN/ConnectViewController.swift b/ios/MullvadVPN/ConnectViewController.swift
index b5cbb08d05..f8968b1ad3 100644
--- a/ios/MullvadVPN/ConnectViewController.swift
+++ b/ios/MullvadVPN/ConnectViewController.swift
@@ -12,20 +12,15 @@ import Logging
class ConnectViewController: UIViewController, RootContainment, TunnelObserver
{
- @IBOutlet var secureLabel: UILabel!
- @IBOutlet var countryLabel: UILabel!
- @IBOutlet var cityLabel: UILabel!
- @IBOutlet var connectionPanel: ConnectionPanelView!
- @IBOutlet var buttonsStackView: UIStackView!
+ private lazy var mainContentView: ConnectMainContentView = {
+ let view = ConnectMainContentView(frame: UIScreen.main.bounds)
+ view.translatesAutoresizingMaskIntoConstraints = false
+ return view
+ }()
private var relayConstraints: RelayConstraints?
private let logger = Logger(label: "ConnectViewController")
-
- private let connectButton = AppButton(style: .success)
- private let selectLocationButton = AppButton(style: .translucentNeutral)
- private let splitDisconnectButtonView = DisconnectSplitButton()
-
private let alertPresenter = AlertPresenter()
override var preferredStatusBarStyle: UIStatusBarStyle {
@@ -49,9 +44,8 @@ class ConnectViewController: UIViewController, RootContainment, TunnelObserver
private var tunnelState: TunnelState = .disconnected {
didSet {
setNeedsHeaderBarStyleAppearanceUpdate()
- updateSecureLabel()
updateTunnelConnectionInfo()
- updateButtons()
+ updateUserInterfaceForTunnelStateChange()
}
}
@@ -60,19 +54,20 @@ class ConnectViewController: UIViewController, RootContainment, TunnelObserver
override func viewDidLoad() {
super.viewDidLoad()
- for button in [connectButton, selectLocationButton] {
- button.titleLabel?.font = UIFont.systemFont(ofSize: 18, weight: .semibold)
- }
-
- selectLocationButton.accessibilityIdentifier = "SelectLocationButton"
- splitDisconnectButtonView.primaryButton.accessibilityIdentifier = "DisconnectButton"
+ mainContentView.connectionPanel.collapseButton.addTarget(self, action: #selector(handleConnectionPanelButton(_:)), for: .touchUpInside)
+ mainContentView.connectButton.addTarget(self, action: #selector(handleConnect(_:)), for: .touchUpInside)
+ mainContentView.splitDisconnectButton.primaryButton.addTarget(self, action: #selector(handleDisconnect(_:)), for: .touchUpInside)
+ mainContentView.splitDisconnectButton.secondaryButton.addTarget(self, action: #selector(handleReconnect(_:)), for: .touchUpInside)
- connectionPanel.collapseButton.addTarget(self, action: #selector(handleConnectionPanelButton(_:)), for: .touchUpInside)
- connectButton.addTarget(self, action: #selector(handleConnect(_:)), for: .touchUpInside)
- splitDisconnectButtonView.primaryButton.addTarget(self, action: #selector(handleDisconnect(_:)), for: .touchUpInside)
- splitDisconnectButtonView.secondaryButton.addTarget(self, action: #selector(handleReconnect(_:)), for: .touchUpInside)
+ mainContentView.selectLocationButton.addTarget(self, action: #selector(handleSelectLocation(_:)), for: .touchUpInside)
- selectLocationButton.addTarget(self, action: #selector(handleSelectLocation(_:)), for: .touchUpInside)
+ view.addSubview(mainContentView)
+ NSLayoutConstraint.activate([
+ mainContentView.topAnchor.constraint(equalTo: view.topAnchor),
+ mainContentView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
+ mainContentView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
+ mainContentView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
+ ])
TunnelManager.shared.addObserver(self)
self.tunnelState = TunnelManager.shared.tunnelState
@@ -100,44 +95,14 @@ class ConnectViewController: UIViewController, RootContainment, TunnelObserver
// MARK: - Private
- private func updateButtons() {
- switch tunnelState {
- case .disconnected, .disconnecting:
- selectLocationButton.setTitle(NSLocalizedString("Select location", comment: ""), for: .normal)
- connectButton.setTitle(NSLocalizedString("Secure connection", comment: ""), for: .normal)
-
- setArrangedButtons([selectLocationButton, connectButton])
-
- case .connecting:
- selectLocationButton.setTitle(NSLocalizedString("Switch location", comment: ""), for: .normal)
- splitDisconnectButtonView.primaryButton.setTitle(NSLocalizedString("Cancel", comment: ""), for: .normal)
-
- setArrangedButtons([selectLocationButton, splitDisconnectButtonView])
-
- case .connected, .reconnecting:
- selectLocationButton.setTitle(NSLocalizedString("Switch location", comment: ""), for: .normal)
- splitDisconnectButtonView.primaryButton.setTitle(NSLocalizedString("Disconnect", comment: ""), for: .normal)
-
- setArrangedButtons([selectLocationButton, splitDisconnectButtonView])
- }
- }
-
- private func setArrangedButtons(_ newButtons: [UIView]) {
- buttonsStackView.arrangedSubviews.forEach { (button) in
- if !newButtons.contains(button) {
- buttonsStackView.removeArrangedSubview(button)
- button.removeFromSuperview()
- }
- }
-
- newButtons.forEach { (button) in
- buttonsStackView.addArrangedSubview(button)
- }
- }
+ private func updateUserInterfaceForTunnelStateChange() {
+ mainContentView.secureLabel.text = tunnelState.localizedTitleForSecureLabel.uppercased()
+ mainContentView.secureLabel.textColor = tunnelState.textColorForSecureLabel
- private func updateSecureLabel() {
- secureLabel.text = tunnelState.textForSecureLabel().uppercased()
- secureLabel.textColor = tunnelState.textColorForSecureLabel()
+ mainContentView.connectButton.setTitle(tunnelState.localizedTitleForConnectButton, for: .normal)
+ mainContentView.selectLocationButton.setTitle(tunnelState.localizedTitleForSelectLocationButton, for: .normal)
+ mainContentView.splitDisconnectButton.primaryButton.setTitle(tunnelState.localizedTitleForDisconnectButton, for: .normal)
+ mainContentView.setActionButtons(tunnelState.actionButtons)
}
private func attributedStringForLocation(string: String) -> NSAttributedString {
@@ -152,21 +117,21 @@ class ConnectViewController: UIViewController, RootContainment, TunnelObserver
switch tunnelState {
case .connected(let connectionInfo),
.reconnecting(let connectionInfo):
- cityLabel.attributedText = attributedStringForLocation(string: connectionInfo.location.city)
- countryLabel.attributedText = attributedStringForLocation(string: connectionInfo.location.country)
+ mainContentView.cityLabel.attributedText = attributedStringForLocation(string: connectionInfo.location.city)
+ mainContentView.countryLabel.attributedText = attributedStringForLocation(string: connectionInfo.location.country)
- connectionPanel.dataSource = ConnectionPanelData(
+ mainContentView.connectionPanel.dataSource = ConnectionPanelData(
inAddress: "\(connectionInfo.ipv4Relay) UDP",
outAddress: nil
)
- connectionPanel.isHidden = false
- connectionPanel.collapseButton.setTitle(connectionInfo.hostname, for: .normal)
+ mainContentView.connectionPanel.isHidden = false
+ mainContentView.connectionPanel.collapseButton.setTitle(connectionInfo.hostname, for: .normal)
case .connecting, .disconnected, .disconnecting:
- cityLabel.attributedText = attributedStringForLocation(string: " ")
- countryLabel.attributedText = attributedStringForLocation(string: " ")
- connectionPanel.dataSource = nil
- connectionPanel.isHidden = true
+ mainContentView.cityLabel.attributedText = attributedStringForLocation(string: " ")
+ mainContentView.countryLabel.attributedText = attributedStringForLocation(string: " ")
+ mainContentView.connectionPanel.dataSource = nil
+ mainContentView.connectionPanel.isHidden = true
}
}
@@ -304,7 +269,7 @@ class ConnectViewController: UIViewController, RootContainment, TunnelObserver
// MARK: - Actions
@objc func handleConnectionPanelButton(_ sender: Any) {
- connectionPanel.toggleConnectionInfoVisibility()
+ mainContentView.connectionPanel.toggleConnectionInfoVisibility()
}
@objc func handleConnect(_ sender: Any) {
@@ -331,7 +296,7 @@ class ConnectViewController: UIViewController, RootContainment, TunnelObserver
private extension TunnelState {
- func textColorForSecureLabel() -> UIColor {
+ var textColorForSecureLabel: UIColor {
switch self {
case .connecting, .reconnecting:
return .white
@@ -344,7 +309,7 @@ private extension TunnelState {
}
}
- func textForSecureLabel() -> String {
+ var localizedTitleForSecureLabel: String {
switch self {
case .connecting, .reconnecting:
return NSLocalizedString("Creating secure connection", comment: "")
@@ -357,4 +322,38 @@ private extension TunnelState {
}
}
+ var localizedTitleForSelectLocationButton: String? {
+ switch self {
+ case .disconnected, .disconnecting:
+ return NSLocalizedString("Select location", comment: "")
+ case .connecting, .connected, .reconnecting:
+ return NSLocalizedString("Switch location", comment: "")
+ }
+ }
+
+ var localizedTitleForConnectButton: String? {
+ return NSLocalizedString("Secure connection", comment: "")
+ }
+
+ var localizedTitleForDisconnectButton: String? {
+ switch self {
+ case .connecting:
+ return NSLocalizedString("Cancel", comment: "")
+ case .connected, .reconnecting:
+ return NSLocalizedString("Disconnect", comment: "")
+ case .disconnecting, .disconnected:
+ return nil
+ }
+ }
+
+ var actionButtons: [ConnectMainContentView.ActionButton] {
+ switch self {
+ case .disconnected, .disconnecting:
+ return [.selectLocation, .connect]
+
+ case .connecting, .connected, .reconnecting:
+ return [.selectLocation, .disconnect]
+ }
+ }
+
}
diff --git a/ios/MullvadVPN/ConnectViewController.xib b/ios/MullvadVPN/ConnectViewController.xib
deleted file mode 100644
index ff2f253c22..0000000000
--- a/ios/MullvadVPN/ConnectViewController.xib
+++ /dev/null
@@ -1,113 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16097.2" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
- <device id="retina6_1" orientation="portrait" appearance="light"/>
- <dependencies>
- <deployment identifier="iOS"/>
- <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
- <capability name="Named colors" minToolsVersion="9.0"/>
- <capability name="Safe area layout guides" minToolsVersion="9.0"/>
- <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
- </dependencies>
- <objects>
- <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="ConnectViewController" customModule="MullvadVPN" customModuleProvider="target">
- <connections>
- <outlet property="buttonsStackView" destination="tVZ-jT-d0h" id="IRm-TE-iJs"/>
- <outlet property="cityLabel" destination="8Qe-Us-gxU" id="qAs-MJ-Quv"/>
- <outlet property="connectionPanel" destination="h6t-ky-9gm" id="hOY-G9-IVQ"/>
- <outlet property="countryLabel" destination="neK-xr-40H" id="tNC-OD-PTx"/>
- <outlet property="secureLabel" destination="dus-AY-JC0" id="PMW-GI-4ga"/>
- <outlet property="view" destination="bFH-8p-vRl" id="jVw-4H-ccF"/>
- </connections>
- </placeholder>
- <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
- <view contentMode="scaleToFill" id="bFH-8p-vRl">
- <rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
- <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
- <subviews>
- <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="SON-pD-ZGn">
- <rect key="frame" x="24" y="502" width="366" height="212"/>
- <subviews>
- <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="SECURE CONNECTION" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dus-AY-JC0">
- <rect key="frame" x="0.0" y="0.0" width="366" height="24"/>
- <accessibility key="accessibilityConfiguration" identifier="SecureConnectionLabel"/>
- <fontDescription key="fontDescription" type="boldSystem" pointSize="20"/>
- <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
- <nil key="highlightedColor"/>
- </label>
- <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="daZ-9L-RFn">
- <rect key="frame" x="0.0" y="32" width="366" height="82"/>
- <subviews>
- <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Stockholm" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="8Qe-Us-gxU">
- <rect key="frame" x="0.0" y="0.0" width="366" height="41"/>
- <fontDescription key="fontDescription" type="boldSystem" pointSize="34"/>
- <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
- <nil key="highlightedColor"/>
- </label>
- <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Sweden" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsLetterSpacingToFitWidth="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="neK-xr-40H">
- <rect key="frame" x="0.0" y="41" width="366" height="41"/>
- <fontDescription key="fontDescription" type="boldSystem" pointSize="34"/>
- <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
- <nil key="highlightedColor"/>
- </label>
- </subviews>
- </stackView>
- <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="mAs-zy-aF9">
- <rect key="frame" x="0.0" y="122" width="366" height="90"/>
- <subviews>
- <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="h6t-ky-9gm" customClass="ConnectionPanelView" customModule="MullvadVPN" customModuleProvider="target">
- <rect key="frame" x="0.0" y="0.0" width="366" height="90"/>
- <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
- <constraints>
- <constraint firstAttribute="height" constant="90" placeholder="YES" id="sqc-lF-rCp"/>
- </constraints>
- </view>
- </subviews>
- <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
- <constraints>
- <constraint firstAttribute="bottom" secondItem="h6t-ky-9gm" secondAttribute="bottom" id="gde-uU-FXR"/>
- <constraint firstAttribute="trailing" secondItem="h6t-ky-9gm" secondAttribute="trailing" id="hyx-Rc-2zS"/>
- <constraint firstItem="h6t-ky-9gm" firstAttribute="leading" secondItem="mAs-zy-aF9" secondAttribute="leading" id="ivP-qV-nIn"/>
- <constraint firstItem="h6t-ky-9gm" firstAttribute="top" secondItem="mAs-zy-aF9" secondAttribute="top" id="zG4-Um-x5m"/>
- </constraints>
- </view>
- </subviews>
- </stackView>
- <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="82Y-rR-Igf">
- <rect key="frame" x="24" y="738" width="366" height="100"/>
- <subviews>
- <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="fillEqually" spacing="16" translatesAutoresizingMaskIntoConstraints="NO" id="tVZ-jT-d0h">
- <rect key="frame" x="0.0" y="0.0" width="366" height="100"/>
- <constraints>
- <constraint firstAttribute="height" constant="100" placeholder="YES" id="VS7-pq-Idz"/>
- </constraints>
- </stackView>
- </subviews>
- <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
- <constraints>
- <constraint firstItem="tVZ-jT-d0h" firstAttribute="top" secondItem="82Y-rR-Igf" secondAttribute="top" id="1YE-mx-7t7"/>
- <constraint firstItem="tVZ-jT-d0h" firstAttribute="leading" secondItem="82Y-rR-Igf" secondAttribute="leading" id="Dab-DT-04W"/>
- <constraint firstAttribute="bottom" secondItem="tVZ-jT-d0h" secondAttribute="bottom" id="Sjj-UF-9Xr"/>
- <constraint firstAttribute="trailing" secondItem="tVZ-jT-d0h" secondAttribute="trailing" id="cOe-Ju-VcI"/>
- </constraints>
- </view>
- </subviews>
- <color key="backgroundColor" name="Primary"/>
- <constraints>
- <constraint firstItem="Rzj-xL-crv" firstAttribute="bottom" secondItem="82Y-rR-Igf" secondAttribute="bottom" constant="24" id="D4A-Ij-Eip"/>
- <constraint firstItem="Rzj-xL-crv" firstAttribute="trailing" secondItem="82Y-rR-Igf" secondAttribute="trailing" constant="24" id="DPX-JT-0Bx"/>
- <constraint firstItem="SON-pD-ZGn" firstAttribute="leading" secondItem="bFH-8p-vRl" secondAttribute="leadingMargin" id="LSL-Go-oXb"/>
- <constraint firstItem="82Y-rR-Igf" firstAttribute="leading" secondItem="Rzj-xL-crv" secondAttribute="leading" constant="24" id="UWY-XY-Njv"/>
- <constraint firstAttribute="trailingMargin" secondItem="SON-pD-ZGn" secondAttribute="trailing" id="Vhs-Sr-QUI"/>
- <constraint firstItem="82Y-rR-Igf" firstAttribute="top" secondItem="SON-pD-ZGn" secondAttribute="bottom" constant="24" id="WhU-py-uqj"/>
- </constraints>
- <edgeInsets key="layoutMargins" top="0.0" left="24" bottom="24" right="24"/>
- <viewLayoutGuide key="safeArea" id="Rzj-xL-crv"/>
- <point key="canvasLocation" x="139" y="153"/>
- </view>
- </objects>
- <resources>
- <namedColor name="Primary">
- <color red="0.16078431372549021" green="0.30196078431372547" blue="0.45098039215686275" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
- </namedColor>
- </resources>
-</document>
diff --git a/ios/MullvadVPN/DisconnectSplitButton.swift b/ios/MullvadVPN/DisconnectSplitButton.swift
index 61ac11579a..09fa5424cd 100644
--- a/ios/MullvadVPN/DisconnectSplitButton.swift
+++ b/ios/MullvadVPN/DisconnectSplitButton.swift
@@ -10,13 +10,16 @@ import Foundation
import UIKit
class DisconnectSplitButton: UIView {
+
+ private var secondaryButtonSize: CGSize {
+ return CGSize(width: 42, height: 42)
+ }
+
let primaryButton = AppButton(style: .translucentDangerSplitLeft)
- var secondaryButton = AppButton(style: .translucentDangerSplitRight)
+ let secondaryButton = AppButton(style: .translucentDangerSplitRight)
private let stackView: UIStackView
- private var secondaryButtonObserver: NSObjectProtocol?
-
init() {
stackView = UIStackView(arrangedSubviews: [primaryButton, secondaryButton])
stackView.translatesAutoresizingMaskIntoConstraints = false
@@ -26,34 +29,23 @@ class DisconnectSplitButton: UIView {
stackView.spacing = 1
primaryButton.titleLabel?.font = UIFont.systemFont(ofSize: 18, weight: .semibold)
- primaryButton.setContentHuggingPriority(.defaultLow, for: .horizontal)
- primaryButton.setContentHuggingPriority(.defaultLow, for: .vertical)
- primaryButton.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
- primaryButton.setContentCompressionResistancePriority(.defaultHigh, for: .vertical)
-
secondaryButton.setImage(UIImage(named: "IconReload"), for: .normal)
- secondaryButton.setContentHuggingPriority(.defaultHigh, for: .horizontal)
- secondaryButton.setContentHuggingPriority(.defaultHigh, for: .vertical)
- secondaryButton.setContentCompressionResistancePriority(UILayoutPriority(50), for: .horizontal)
- secondaryButton.setContentCompressionResistancePriority(UILayoutPriority(50), for: .vertical)
super.init(frame: .zero)
addSubview(stackView)
- secondaryButtonObserver = secondaryButton.observe(\.bounds, options: [.new]) { [weak self] (button, change) in
- self?.adjustTitleLabelPosition()
- }
-
NSLayoutConstraint.activate([
stackView.leadingAnchor.constraint(equalTo: leadingAnchor),
stackView.trailingAnchor.constraint(equalTo: trailingAnchor),
stackView.topAnchor.constraint(equalTo: topAnchor),
stackView.bottomAnchor.constraint(equalTo: bottomAnchor),
- primaryButton.heightAnchor.constraint(equalTo: secondaryButton.heightAnchor),
- secondaryButton.widthAnchor.constraint(equalTo: secondaryButton.heightAnchor)
+ secondaryButton.widthAnchor.constraint(equalToConstant: self.secondaryButtonSize.width),
+ secondaryButton.heightAnchor.constraint(equalToConstant: self.secondaryButtonSize.height)
])
+
+ adjustTitleLabelPosition()
}
required init?(coder: NSCoder) {
@@ -62,7 +54,7 @@ class DisconnectSplitButton: UIView {
private func adjustTitleLabelPosition() {
var contentInsets = AppButton.defaultContentInsets
- contentInsets.left = secondaryButton.frame.width + stackView.spacing
+ contentInsets.left = stackView.spacing + self.secondaryButtonSize.width
contentInsets.right = 0
primaryButton.contentEdgeInsets = contentInsets
diff --git a/ios/MullvadVPN/RootContainerViewController.swift b/ios/MullvadVPN/RootContainerViewController.swift
index 465fff6f1e..9a3fa411fa 100644
--- a/ios/MullvadVPN/RootContainerViewController.swift
+++ b/ios/MullvadVPN/RootContainerViewController.swift
@@ -377,8 +377,8 @@ class RootContainerViewController: UIViewController {
/// Updates additional safe area insets to push the child views below the header bar
private func updateAdditionalSafeAreaInsetsIfNeeded() {
- // Reset top inset if header bar is invisible
- let insetTop = headerBarHidden ? 0 : headerBarView.frame.height
+ let offsetTop = view.safeAreaInsets.top - additionalSafeAreaInsets.top
+ let insetTop = headerBarHidden ? 0 : headerBarView.frame.height - offsetTop
if additionalSafeAreaInsets.top != insetTop {
additionalSafeAreaInsets.top = insetTop