summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorBug Magnet <marco.nikic@mullvad.net>2023-10-27 10:24:50 +0200
committerBug Magnet <marco.nikic@mullvad.net>2023-10-27 10:24:50 +0200
commit7e9bb04524cfc2520a56295e985a58a6542cabca (patch)
tree94a9000c9b178d2344b5e30fad4a914b78b10d7e
parentae31f2cad7b14e4590666d9d45a737691cc7925d (diff)
parented4d997cad9f704998f8e15a6fc442e582f16e85 (diff)
downloadmullvadvpn-7e9bb04524cfc2520a56295e985a58a6542cabca.tar.xz
mullvadvpn-7e9bb04524cfc2520a56295e985a58a6542cabca.zip
Merge branch 'make-change-log-scrollable-ios-359'
-rw-r--r--ios/MullvadVPN/UI appearance/UIMetrics.swift5
-rw-r--r--ios/MullvadVPN/View controllers/Alert/AlertViewController.swift175
2 files changed, 120 insertions, 60 deletions
diff --git a/ios/MullvadVPN/UI appearance/UIMetrics.swift b/ios/MullvadVPN/UI appearance/UIMetrics.swift
index 4d5c420000..8c23dedde5 100644
--- a/ios/MullvadVPN/UI appearance/UIMetrics.swift
+++ b/ios/MullvadVPN/UI appearance/UIMetrics.swift
@@ -31,8 +31,11 @@ enum UIMetrics {
trailing: 16
)
- /// Spacing between views in container (main view) in `CustomAlertViewController`
+ /// Spacing between view containers in `CustomAlertViewController`
static let containerSpacing: CGFloat = 16
+
+ /// Spacing between view containers in `CustomAlertViewController`
+ static let interContainerSpacing: CGFloat = 4
}
enum DimmingView {
diff --git a/ios/MullvadVPN/View controllers/Alert/AlertViewController.swift b/ios/MullvadVPN/View controllers/Alert/AlertViewController.swift
index d45e16bcf6..96142fa502 100644
--- a/ios/MullvadVPN/View controllers/Alert/AlertViewController.swift
+++ b/ios/MullvadVPN/View controllers/Alert/AlertViewController.swift
@@ -43,12 +43,34 @@ class AlertViewController: UIViewController {
typealias Handler = () -> Void
var onDismiss: Handler?
- private let containerView: UIStackView = {
- let view = UIStackView()
+ private let scrollView = UIScrollView()
+ private var scrollViewHeightConstraint: NSLayoutConstraint!
+ private let presentation: AlertPresentation
+
+ private let viewContainer: UIView = {
+ let view = UIView()
- view.axis = .vertical
view.backgroundColor = .secondaryColor
view.layer.cornerRadius = 11
+
+ return view
+ }()
+
+ private let buttonView: UIStackView = {
+ let view = UIStackView()
+
+ view.axis = .vertical
+ view.spacing = UIMetrics.CustomAlert.containerSpacing
+ view.isLayoutMarginsRelativeArrangement = true
+ view.directionalLayoutMargins = UIMetrics.CustomAlert.containerMargins
+
+ return view
+ }()
+
+ private let contentView: UIStackView = {
+ let view = UIStackView()
+
+ view.axis = .vertical
view.spacing = UIMetrics.CustomAlert.containerSpacing
view.isLayoutMarginsRelativeArrangement = true
view.directionalLayoutMargins = UIMetrics.CustomAlert.containerMargins
@@ -59,18 +81,45 @@ class AlertViewController: UIViewController {
private var handlers = [UIButton: Handler]()
init(presentation: AlertPresentation) {
+ self.presentation = presentation
+
super.init(nibName: nil, bundle: nil)
- setUp(
- header: presentation.header,
- title: presentation.title,
- icon: presentation.icon
- ) {
- if let message = presentation.attributedMessage {
- addMessage(message)
- } else if let message = presentation.message {
- addMessage(message)
- }
+ modalPresentationStyle = .overFullScreen
+ modalTransitionStyle = .crossDissolve
+ }
+
+ required init?(coder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+ override func viewDidLayoutSubviews() {
+ super.viewDidLayoutSubviews()
+
+ view.layoutIfNeeded()
+ scrollViewHeightConstraint.constant = scrollView.contentSize.height
+
+ adjustButtonMargins()
+ }
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+
+ view.backgroundColor = .black.withAlphaComponent(0.5)
+
+ setContent()
+ setConstraints()
+ }
+
+ private func setContent() {
+ presentation.icon.flatMap { addIcon($0) }
+ presentation.header.flatMap { addHeader($0) }
+ presentation.title.flatMap { addTitle($0) }
+
+ if let message = presentation.attributedMessage {
+ addMessage(message)
+ } else if let message = presentation.message {
+ addMessage(message)
}
presentation.buttons.forEach { action in
@@ -80,65 +129,63 @@ class AlertViewController: UIViewController {
handler: action.handler
)
}
- }
- required init?(coder: NSCoder) {
- fatalError("init(coder:) has not been implemented")
+ // Icon only alerts should have equal top and bottom margin.
+ if presentation.icon != nil, contentView.arrangedSubviews.count == 1 {
+ contentView.directionalLayoutMargins.bottom = UIMetrics.CustomAlert.containerMargins.top
+ }
}
- // This code runs before viewDidLoad(). As such, no implicit calls to self.view should be made before this point.
- private func setUp(header: String?, title: String?, icon: AlertIcon?, addMessageCallback: () -> Void) {
- modalPresentationStyle = .overFullScreen
- modalTransitionStyle = .crossDissolve
-
- icon.flatMap { addIcon($0) }
- header.flatMap { addHeader($0) }
- title.flatMap { addTitle($0) }
- addMessageCallback()
-
- containerView.arrangedSubviews.last.flatMap {
- containerView.setCustomSpacing(UIMetrics.CustomAlert.containerMargins.top, after: $0)
+ private func setConstraints() {
+ viewContainer.addConstrainedSubviews([scrollView, buttonView]) {
+ scrollView.pinEdgesToSuperview(.all().excluding(.bottom))
+ buttonView.pinEdgesToSuperview(.all().excluding(.top))
+ buttonView.topAnchor.constraint(equalTo: scrollView.bottomAnchor)
}
- // Icon only alerts should have equal top and bottom margin.
- if icon != nil, containerView.arrangedSubviews.count == 1 {
- containerView.directionalLayoutMargins.bottom = UIMetrics.CustomAlert.containerMargins.top
+ scrollView.addConstrainedSubviews([contentView]) {
+ contentView.pinEdgesToSuperview(.all())
+ contentView.widthAnchor.constraint(equalTo: scrollView.widthAnchor)
}
- }
- override func viewDidLoad() {
- super.viewDidLoad()
-
- view.backgroundColor = .black.withAlphaComponent(0.5)
+ scrollViewHeightConstraint = scrollView.heightAnchor.constraint(equalToConstant: 0).withPriority(.defaultLow)
+ scrollViewHeightConstraint.isActive = true
- view.addConstrainedSubviews([containerView]) {
- containerView.centerXAnchor.constraint(equalTo: view.centerXAnchor)
- containerView.centerYAnchor.constraint(equalTo: view.centerYAnchor)
+ view.addConstrainedSubviews([viewContainer]) {
+ viewContainer.centerXAnchor.constraint(equalTo: view.centerXAnchor)
+ viewContainer.centerYAnchor.constraint(equalTo: view.centerYAnchor)
- containerView.widthAnchor
+ viewContainer.widthAnchor
.constraint(lessThanOrEqualToConstant: UIMetrics.preferredFormSheetContentSize.width)
- containerView.leadingAnchor
+ viewContainer.topAnchor
+ .constraint(greaterThanOrEqualTo: view.layoutMarginsGuide.topAnchor)
+ .withPriority(.defaultHigh)
+
+ view.layoutMarginsGuide.bottomAnchor
+ .constraint(greaterThanOrEqualTo: viewContainer.bottomAnchor)
+ .withPriority(.defaultHigh)
+
+ viewContainer.leadingAnchor
.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor)
.withPriority(.defaultHigh)
view.layoutMarginsGuide.trailingAnchor
- .constraint(equalTo: containerView.trailingAnchor)
+ .constraint(equalTo: viewContainer.trailingAnchor)
.withPriority(.defaultHigh)
}
}
- func addAction(title: String, style: AlertActionStyle, handler: (() -> Void)? = nil) {
- // The presence of a button should reset any custom button margin to default.
- containerView.directionalLayoutMargins.bottom = UIMetrics.CustomAlert.containerMargins.bottom
-
- let button = AppButton(style: style.buttonStyle)
-
- button.setTitle(title, for: .normal)
- button.addTarget(self, action: #selector(didTapButton), for: .touchUpInside)
+ private func adjustButtonMargins() {
+ if !buttonView.arrangedSubviews.isEmpty {
+ // The presence of a button should yield a custom top margin.
+ buttonView.directionalLayoutMargins.top = UIMetrics.CustomAlert.interContainerSpacing
- containerView.addArrangedSubview(button)
- handler.flatMap { handlers[button] = $0 }
+ // Buttons below scrollable content should have more margin.
+ if scrollView.contentSize.height > scrollView.bounds.size.height {
+ buttonView.directionalLayoutMargins.top = UIMetrics.CustomAlert.containerSpacing
+ }
+ }
}
private func addHeader(_ title: String) {
@@ -151,8 +198,8 @@ class AlertViewController: UIViewController {
header.textAlignment = .center
header.numberOfLines = 0
- containerView.addArrangedSubview(header)
- containerView.setCustomSpacing(16, after: header)
+ contentView.addArrangedSubview(header)
+ contentView.setCustomSpacing(16, after: header)
}
private func addTitle(_ title: String) {
@@ -164,8 +211,8 @@ class AlertViewController: UIViewController {
label.adjustsFontForContentSizeCategory = true
label.numberOfLines = 0
- containerView.addArrangedSubview(label)
- containerView.setCustomSpacing(8, after: label)
+ contentView.addArrangedSubview(label)
+ contentView.setCustomSpacing(8, after: label)
}
private func addMessage(_ message: String) {
@@ -185,7 +232,7 @@ class AlertViewController: UIViewController {
label.adjustsFontForContentSizeCategory = true
label.numberOfLines = 0
- containerView.addArrangedSubview(label)
+ contentView.addArrangedSubview(label)
}
private func addMessage(_ message: NSAttributedString) {
@@ -196,12 +243,22 @@ class AlertViewController: UIViewController {
label.adjustsFontForContentSizeCategory = true
label.numberOfLines = 0
- containerView.addArrangedSubview(label)
+ contentView.addArrangedSubview(label)
}
private func addIcon(_ icon: AlertIcon) {
let iconView = icon == .spinner ? getSpinnerView() : getImageView(for: icon)
- containerView.addArrangedSubview(iconView)
+ contentView.addArrangedSubview(iconView)
+ }
+
+ private func addAction(title: String, style: AlertActionStyle, handler: (() -> Void)? = nil) {
+ let button = AppButton(style: style.buttonStyle)
+
+ button.setTitle(title, for: .normal)
+ button.addTarget(self, action: #selector(didTapButton), for: .touchUpInside)
+
+ buttonView.addArrangedSubview(button)
+ handler.flatMap { handlers[button] = $0 }
}
private func getImageView(for icon: AlertIcon) -> UIView {