From 564e0f09075026f5ee2cfa3e3ffb293db1833083 Mon Sep 17 00:00:00 2001 From: Jon Petersson Date: Fri, 20 Oct 2023 14:02:28 +0200 Subject: [PATCH] Make alert view scrollable --- ios/MullvadVPN/UI appearance/UIMetrics.swift | 5 +- .../Alert/AlertViewController.swift | 96 ++++++++++++++----- 2 files changed, 75 insertions(+), 26 deletions(-) diff --git a/ios/MullvadVPN/UI appearance/UIMetrics.swift b/ios/MullvadVPN/UI appearance/UIMetrics.swift index 4d5c42000094..8c23dedde59e 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 d45e16bcf668..1c9b8c088e80 100644 --- a/ios/MullvadVPN/View controllers/Alert/AlertViewController.swift +++ b/ios/MullvadVPN/View controllers/Alert/AlertViewController.swift @@ -43,12 +43,33 @@ class AlertViewController: UIViewController { typealias Handler = () -> Void var onDismiss: Handler? - private let containerView: UIStackView = { - let view = UIStackView() + let scrollView = UIScrollView() + var scrollViewHeightConstraint: NSLayoutConstraint! + + 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 @@ -96,48 +117,73 @@ class AlertViewController: UIViewController { title.flatMap { addTitle($0) } addMessageCallback() - containerView.arrangedSubviews.last.flatMap { - containerView.setCustomSpacing(UIMetrics.CustomAlert.containerMargins.top, after: $0) - } - // Icon only alerts should have equal top and bottom margin. - if icon != nil, containerView.arrangedSubviews.count == 1 { - containerView.directionalLayoutMargins.bottom = UIMetrics.CustomAlert.containerMargins.top + if icon != nil, contentView.arrangedSubviews.count == 1 { + contentView.directionalLayoutMargins.bottom = UIMetrics.CustomAlert.containerMargins.top } } + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + + view.layoutIfNeeded() + scrollViewHeightConstraint.constant = scrollView.contentSize.height + } + override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .black.withAlphaComponent(0.5) - view.addConstrainedSubviews([containerView]) { - containerView.centerXAnchor.constraint(equalTo: view.centerXAnchor) - containerView.centerYAnchor.constraint(equalTo: view.centerYAnchor) + viewContainer.addConstrainedSubviews([scrollView, buttonView]) { + scrollView.pinEdgesToSuperview(.all().excluding(.bottom)) + buttonView.pinEdgesToSuperview(.all().excluding(.top)) + buttonView.topAnchor.constraint(equalTo: scrollView.bottomAnchor) + } - containerView.widthAnchor + scrollView.addConstrainedSubviews([contentView]) { + contentView.pinEdgesToSuperview(.all()) + contentView.widthAnchor.constraint(equalTo: scrollView.widthAnchor) + } + + scrollViewHeightConstraint = scrollView.heightAnchor.constraint(equalToConstant: 0).withPriority(.defaultLow) + scrollViewHeightConstraint.isActive = true + + view.addConstrainedSubviews([viewContainer]) { + viewContainer.centerXAnchor.constraint(equalTo: view.centerXAnchor) + viewContainer.centerYAnchor.constraint(equalTo: view.centerYAnchor) + + 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 + // The presence of a button should yield a custom top margin. + buttonView.directionalLayoutMargins.top = UIMetrics.CustomAlert.interContainerSpacing let button = AppButton(style: style.buttonStyle) button.setTitle(title, for: .normal) button.addTarget(self, action: #selector(didTapButton), for: .touchUpInside) - containerView.addArrangedSubview(button) + buttonView.addArrangedSubview(button) handler.flatMap { handlers[button] = $0 } } @@ -151,8 +197,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 +210,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 +231,7 @@ class AlertViewController: UIViewController { label.adjustsFontForContentSizeCategory = true label.numberOfLines = 0 - containerView.addArrangedSubview(label) + contentView.addArrangedSubview(label) } private func addMessage(_ message: NSAttributedString) { @@ -196,12 +242,12 @@ 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 getImageView(for icon: AlertIcon) -> UIView {