diff options
6 files changed, 86 insertions, 423 deletions
diff --git a/ios/Assets/Localizable.xcstrings b/ios/Assets/Localizable.xcstrings index 83e34f943a..c38874413e 100644 --- a/ios/Assets/Localizable.xcstrings +++ b/ios/Assets/Localizable.xcstrings @@ -11486,125 +11486,6 @@ } } }, - "Collapse" : { - "extractionState" : "stale", - "localizations" : { - "da" : { - "stringUnit" : { - "state" : "translated", - "value" : "Skjul" - } - }, - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "zuklappen" - } - }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Contraer" - } - }, - "fi" : { - "stringUnit" : { - "state" : "translated", - "value" : "Kutista" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Réduire" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Comprimi" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "を折りたたむ" - } - }, - "ko" : { - "stringUnit" : { - "state" : "translated", - "value" : "접기" - } - }, - "my" : { - "stringUnit" : { - "state" : "translated", - "value" : "ကို ချုံ့ရန်" - } - }, - "nb" : { - "stringUnit" : { - "state" : "translated", - "value" : "Skjul" - } - }, - "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "inklappen" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Zwiń" - } - }, - "pt" : { - "stringUnit" : { - "state" : "translated", - "value" : "Colapsar" - } - }, - "ru" : { - "stringUnit" : { - "state" : "translated", - "value" : "Свернуть" - } - }, - "sv" : { - "stringUnit" : { - "state" : "translated", - "value" : "Dölj" - } - }, - "th" : { - "stringUnit" : { - "state" : "translated", - "value" : "ยุบ" - } - }, - "tr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Daralt" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "收起" - } - }, - "zh-Hant" : { - "stringUnit" : { - "state" : "translated", - "value" : "收合" - } - } - } - }, "Collapse %@" : { "localizations" : { "da" : { @@ -12088,43 +11969,43 @@ "de" : { "stringUnit" : { "state" : "translated", - "value" : "Connect (Verbinden)" + "value" : "Verbinden" } }, "es" : { "stringUnit" : { "state" : "translated", - "value" : "Connect (Conectar)" + "value" : "Conectar" } }, "fi" : { "stringUnit" : { "state" : "translated", - "value" : "Connect (Yhdistä)" + "value" : "Yhdistä" } }, "fr" : { "stringUnit" : { "state" : "translated", - "value" : "Connect (Connexion)" + "value" : "Connexion" } }, "it" : { "stringUnit" : { "state" : "translated", - "value" : "Connect (Connetti)" + "value" : "Connetti" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "Connect (接続)" + "value" : "接続" } }, "ko" : { "stringUnit" : { "state" : "translated", - "value" : "Connect(연결)" + "value" : "연결" } }, "my" : { @@ -12142,19 +12023,19 @@ "nl" : { "stringUnit" : { "state" : "translated", - "value" : "Connect (Verbinden)" + "value" : "Verbinden" } }, "pl" : { "stringUnit" : { "state" : "translated", - "value" : "Connect (Połącz)" + "value" : "Połącz" } }, "pt" : { "stringUnit" : { "state" : "translated", - "value" : "Connect (Ligar)" + "value" : "Ligar" } }, "ru" : { @@ -12166,31 +12047,31 @@ "sv" : { "stringUnit" : { "state" : "translated", - "value" : "Connect (anslut)" + "value" : "Anslut" } }, "th" : { "stringUnit" : { "state" : "translated", - "value" : "Connect (เชื่อมต่อ)" + "value" : "เชื่อมต่อ" } }, "tr" : { "stringUnit" : { "state" : "translated", - "value" : "Connect (bağlan)" + "value" : "Bağlan" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", - "value" : "Connect(连接)" + "value" : "连接" } }, "zh-Hant" : { "stringUnit" : { "state" : "translated", - "value" : "Connect (連線)" + "value" : "連線" } } } @@ -20243,125 +20124,6 @@ } } }, - "Expand" : { - "extractionState" : "stale", - "localizations" : { - "da" : { - "stringUnit" : { - "state" : "translated", - "value" : "Udvid" - } - }, - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "ausklappen" - } - }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Expandir" - } - }, - "fi" : { - "stringUnit" : { - "state" : "translated", - "value" : "Laajenna" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Agrandir" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Espandi" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "を展開する" - } - }, - "ko" : { - "stringUnit" : { - "state" : "translated", - "value" : "펼치기" - } - }, - "my" : { - "stringUnit" : { - "state" : "translated", - "value" : "ကို ဖြန့်ပြရန်" - } - }, - "nb" : { - "stringUnit" : { - "state" : "translated", - "value" : "Utvid" - } - }, - "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "uitklappen" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Rozwiń" - } - }, - "pt" : { - "stringUnit" : { - "state" : "translated", - "value" : "Expandir" - } - }, - "ru" : { - "stringUnit" : { - "state" : "translated", - "value" : "Развернуть" - } - }, - "sv" : { - "stringUnit" : { - "state" : "translated", - "value" : "Visa" - } - }, - "th" : { - "stringUnit" : { - "state" : "translated", - "value" : "ขยาย" - } - }, - "tr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Genişlet" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "展开" - } - }, - "zh-Hant" : { - "stringUnit" : { - "state" : "translated", - "value" : "展開" - } - } - } - }, "Expand %@" : { "localizations" : { "da" : { @@ -41766,125 +41528,6 @@ } } }, - "Refund last purchase with StoreKit2" : { - "extractionState" : "stale", - "localizations" : { - "da" : { - "stringUnit" : { - "state" : "translated", - "value" : "Refusion af sidste køb med StoreKit2" - } - }, - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Letzten Einkauf mit StoreKit2 zurückerstatten" - } - }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Reembolsar última compra con StoreKit2" - } - }, - "fi" : { - "stringUnit" : { - "state" : "translated", - "value" : "Pyydä hyvitystä viimeisimmästä ostoksesta StoreKit2:lla" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Rembourser le dernier achat avec StoreKit2" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Rimborso dell'ultimo acquisto con StoreKit2" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "StoreKit2で行った直近の購入を返金する" - } - }, - "ko" : { - "stringUnit" : { - "state" : "translated", - "value" : "StoreKit2에서 진행한 마지막 결제 환불" - } - }, - "my" : { - "stringUnit" : { - "state" : "translated", - "value" : "StoreKit2 နောက်ဆုံးဝယ်ယူမှုအတွက် ငွေပြန်အမ်းပါ" - } - }, - "nb" : { - "stringUnit" : { - "state" : "translated", - "value" : "Refunder siste kjøp med StoreKit2" - } - }, - "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Laatste aankoop bij StoreKit2 terugbetalen" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Zwróć koszty ostatniego zakupu za pomocą StoreKit2" - } - }, - "pt" : { - "stringUnit" : { - "state" : "translated", - "value" : "Reembolsar a última compra com o StoreKit2" - } - }, - "ru" : { - "stringUnit" : { - "state" : "translated", - "value" : "Вернуть последнюю покупку через StoreKit2" - } - }, - "sv" : { - "stringUnit" : { - "state" : "translated", - "value" : "Återbetala senaste köpet med StoreKit2" - } - }, - "th" : { - "stringUnit" : { - "state" : "translated", - "value" : "คืนเงินการซื้อครั้งสุดท้ายกับ StoreKit2" - } - }, - "tr" : { - "stringUnit" : { - "state" : "translated", - "value" : "StoreKit2 ile yapılan son satın alımı iade et" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "对通过 StoreKit2 进行的上次购买退款" - } - }, - "zh-Hant" : { - "stringUnit" : { - "state" : "translated", - "value" : "透過 StoreKit2 為上次購買進行退款" - } - } - } - }, "Refund successful" : { "localizations" : { "da" : { diff --git a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Cells/ListCellContentView.swift b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Cells/ListCellContentView.swift index fe76da7cbf..b6dbfbafe0 100644 --- a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Cells/ListCellContentView.swift +++ b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Cells/ListCellContentView.swift @@ -104,12 +104,16 @@ class ListCellContentView: UIView, UIContentView, UITextFieldDelegate { secondaryTextLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) addConstrainedSubviews([leadingTextContainer, secondaryTextLabel]) { - leadingTextContainer.pinEdgesToSuperviewMargins(.all().excluding(.trailing)) - secondaryTextLabel.pinEdgesToSuperviewMargins(.all().excluding(.leading)) - secondaryTextLabel.leadingAnchor.constraint( - greaterThanOrEqualToSystemSpacingAfter: leadingTextContainer.trailingAnchor, - multiplier: 1 - ) + if actualConfiguration.secondaryText == nil { + leadingTextContainer.pinEdgesToSuperviewMargins(.all()) + } else { + leadingTextContainer.pinEdgesToSuperviewMargins(.all().excluding(.trailing)) + secondaryTextLabel.pinEdgesToSuperviewMargins(.all().excluding(.leading)) + secondaryTextLabel.leadingAnchor.constraint( + greaterThanOrEqualToSystemSpacingAfter: leadingTextContainer.trailingAnchor, + multiplier: 1 + ) + } } } } diff --git a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Cells/TextCellContentView.swift b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Cells/TextCellContentView.swift index 2b5f7253b5..2d34c7c8a7 100644 --- a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Cells/TextCellContentView.swift +++ b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Cells/TextCellContentView.swift @@ -12,6 +12,7 @@ import UIKit class TextCellContentView: UIView, UIContentView, UIGestureRecognizerDelegate, Sendable { private var textLabel = UILabel() private var textField = CustomTextField() + private var containerView = UIStackView() var configuration: UIContentConfiguration { get { @@ -39,7 +40,6 @@ class TextCellContentView: UIView, UIContentView, UIGestureRecognizerDelegate, S actualConfiguration = configuration super.init(frame: CGRect(x: 0, y: 0, width: 100, height: 44)) - configureSubviews() addSubviews() addTapGestureRecognizer() @@ -51,7 +51,6 @@ class TextCellContentView: UIView, UIContentView, UIGestureRecognizerDelegate, S private func configureSubviews(previousConfiguration: TextCellContentConfiguration? = nil) { guard actualConfiguration != previousConfiguration else { return } - configureTextLabel() configureTextField() configureLayoutMargins() @@ -86,19 +85,45 @@ class TextCellContentView: UIView, UIContentView, UIGestureRecognizerDelegate, S } private func addSubviews() { - textField.setContentHuggingPriority(.defaultLow, for: .horizontal) - textField.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal) + containerView.addArrangedSubview(textLabel) + containerView.addArrangedSubview(textField) + containerView.spacing = 8.0 + textLabel.setContentCompressionResistancePriority(.required, for: .horizontal) - textLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal) - textLabel.setContentCompressionResistancePriority(.defaultHigh + 1, for: .horizontal) + addConstrainedSubviews([containerView]) { + containerView.pinEdgesToSuperviewMargins() + } + } - addConstrainedSubviews([textLabel, textField]) { - textField.pinEdgesToSuperviewMargins(.all().excluding(.leading)) - textLabel.pinEdgesToSuperviewMargins(.all().excluding(.trailing)) - textField.leadingAnchor.constraint(equalToSystemSpacingAfter: textLabel.trailingAnchor, multiplier: 1) + override func didMoveToSuperview() { + super.didMoveToSuperview() + DispatchQueue.main.async { + self.updateAxisIfNeeded() } } + private func updateAxisIfNeeded() { + let newAxis: NSLayoutConstraint.Axis = isVertical ? .vertical : .horizontal + guard newAxis != containerView.axis else { return } + containerView.axis = newAxis + invalidateIntrinsicContentSize() + setNeedsLayout() + superview?.setNeedsLayout() + } + + var isVertical: Bool { + let availableSize = containerView.bounds + guard containerView.bounds.width > 0 else { return false } + let fittingSize = containerView.systemLayoutSizeFitting( + CGSize( + width: CGFloat.greatestFiniteMagnitude, + height: availableSize.height), + withHorizontalFittingPriority: .fittingSizeLevel, + verticalFittingPriority: .fittingSizeLevel + ) + return fittingSize.width > availableSize.width + } + // MARK: - Gesture recognition /// Add tap recognizer that activates the text field on tap anywhere within the content view. diff --git a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/MethodSettings/MethodSettingsViewController.swift b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/MethodSettings/MethodSettingsViewController.swift index 11636de197..de0770f163 100644 --- a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/MethodSettings/MethodSettingsViewController.swift +++ b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/MethodSettings/MethodSettingsViewController.swift @@ -120,12 +120,8 @@ class MethodSettingsViewController: UITableViewController { } override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { - guard let sectionIdentifier = dataSource?.snapshot().sectionIdentifiers[section] else { return nil } - - guard - let headerView = - tableView - .dequeueReusableView(withIdentifier: AccessMethodHeaderFooterReuseIdentifier.primary) + guard let sectionIdentifier = dataSource?.snapshot().sectionIdentifiers[section], + sectionIdentifier.sectionName != nil else { return nil } var contentConfiguration = ListCellContentConfiguration( @@ -135,9 +131,7 @@ class MethodSettingsViewController: UITableViewController { ) contentConfiguration.text = sectionIdentifier.sectionName - headerView.contentConfiguration = contentConfiguration - - return headerView + return contentConfiguration.makeContentView() } override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { @@ -208,7 +202,6 @@ class MethodSettingsViewController: UITableViewController { private func configureDataSource() { tableView.registerReusableViews(from: AccessMethodCellReuseIdentifier.self) - tableView.registerReusableViews(from: AccessMethodHeaderFooterReuseIdentifier.self) dataSource = UITableViewDiffableDataSource( tableView: tableView, diff --git a/ios/MullvadVPN/View controllers/Account/AccountExpiryRow.swift b/ios/MullvadVPN/View controllers/Account/AccountExpiryRow.swift index 847f468f2c..69473eb4ce 100644 --- a/ios/MullvadVPN/View controllers/Account/AccountExpiryRow.swift +++ b/ios/MullvadVPN/View controllers/Account/AccountExpiryRow.swift @@ -40,12 +40,13 @@ class AccountExpiryRow: UIView { private let textLabel: UILabel = { let textLabel = UILabel() - textLabel.translatesAutoresizingMaskIntoConstraints = false textLabel.text = NSLocalizedString("Paid until", comment: "") textLabel.font = .mullvadTiny textLabel.numberOfLines = 0 textLabel.adjustsFontForContentSizeCategory = true textLabel.textColor = UIColor(white: 1.0, alpha: 0.6) + textLabel.setContentCompressionResistancePriority(.required, for: .horizontal) + textLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal) return textLabel }() @@ -65,24 +66,23 @@ class AccountExpiryRow: UIView { activityIndicator.translatesAutoresizingMaskIntoConstraints = false activityIndicator.tintColor = .white activityIndicator.setContentHuggingPriority(.defaultHigh, for: .horizontal) - activityIndicator.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal) + activityIndicator.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) + activityIndicator.setContentHuggingPriority(.defaultLow, for: .horizontal) return activityIndicator }() override init(frame: CGRect) { super.init(frame: frame) + let stackView: UIStackView = { + let stackView = UIStackView(arrangedSubviews: [textLabel, activityIndicator, UIView()]) + stackView.axis = .horizontal + stackView.distribution = .fill + stackView.spacing = UIMetrics.padding8 + return stackView + }() - addConstrainedSubviews([textLabel, activityIndicator, valueLabel]) { - textLabel.pinEdgesToSuperview(.all().excluding([.trailing, .bottom])) - textLabel.trailingAnchor.constraint( - greaterThanOrEqualTo: activityIndicator.leadingAnchor, - constant: -UIMetrics.padding8 - ) - - activityIndicator.topAnchor.constraint(equalTo: textLabel.topAnchor) - activityIndicator.bottomAnchor.constraint(equalTo: textLabel.bottomAnchor) - activityIndicator.trailingAnchor.constraint(equalTo: trailingAnchor) - + addConstrainedSubviews([stackView, valueLabel]) { + stackView.pinEdgesToSuperview(.all().excluding([.bottom])) valueLabel.pinEdgesToSuperview(.all().excluding(.top)) valueLabel.topAnchor.constraint(equalTo: textLabel.bottomAnchor, constant: UIMetrics.padding8) } diff --git a/ios/MullvadVPN/Views/SpinnerActivityIndicatorView.swift b/ios/MullvadVPN/Views/SpinnerActivityIndicatorView.swift index 5d58db2b91..77166a8287 100644 --- a/ios/MullvadVPN/Views/SpinnerActivityIndicatorView.swift +++ b/ios/MullvadVPN/Views/SpinnerActivityIndicatorView.swift @@ -44,14 +44,13 @@ class SpinnerActivityIndicatorView: UIView { init(style: Style) { self.style = style + super.init(frame: .zero) - let size = style == .custom ? .zero : style.intrinsicSize - - super.init(frame: CGRect(origin: .zero, size: size)) + backgroundColor = .clear + isHidden = true + imageView.contentMode = .scaleAspectFit addSubview(imageView) - isHidden = true - backgroundColor = UIColor.clear } required init?(coder: NSCoder) { @@ -80,7 +79,8 @@ class SpinnerActivityIndicatorView: UIView { let size = style == .custom ? frame.size : style.intrinsicSize - imageView.frame = CGRect(origin: .zero, size: size) + imageView.bounds = CGRect(origin: .zero, size: size) + imageView.center = CGPoint(x: bounds.midX, y: bounds.midY) } func startAnimating() { @@ -100,11 +100,11 @@ class SpinnerActivityIndicatorView: UIView { } private func addAnimation() { - layer.add(createAnimation(), forKey: Self.rotationAnimationKey) + imageView.layer.add(createAnimation(), forKey: Self.rotationAnimationKey) } private func removeAnimation() { - layer.removeAnimation(forKey: Self.rotationAnimationKey) + imageView.layer.removeAnimation(forKey: Self.rotationAnimationKey) } private func registerSceneActivationObserver() { @@ -140,12 +140,10 @@ class SpinnerActivityIndicatorView: UIView { private func createAnimation() -> CABasicAnimation { let animation = CABasicAnimation(keyPath: "transform.rotation") - animation.toValue = NSNumber(value: Double.pi * 2) + animation.toValue = Double.pi * 2 animation.duration = Self.animationDuration - animation.repeatCount = Float.infinity + animation.repeatCount = .infinity animation.timingFunction = CAMediaTimingFunction(name: .linear) - animation.timeOffset = layer.convertTime(CACurrentMediaTime(), from: nil) - return animation } } |
