summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--ios/Assets/Localizable.xcstrings387
-rw-r--r--ios/MullvadVPN/Coordinators/Settings/APIAccess/Cells/ListCellContentView.swift16
-rw-r--r--ios/MullvadVPN/Coordinators/Settings/APIAccess/Cells/TextCellContentView.swift45
-rw-r--r--ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/MethodSettings/MethodSettingsViewController.swift13
-rw-r--r--ios/MullvadVPN/View controllers/Account/AccountExpiryRow.swift26
-rw-r--r--ios/MullvadVPN/Views/SpinnerActivityIndicatorView.swift22
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
}
}