Skip to content

Commit 973ed38

Browse files
committed
Refactor buttons to use UIButton.Configuration
1 parent 0e32a21 commit 973ed38

File tree

8 files changed

+78
-228
lines changed

8 files changed

+78
-228
lines changed

Diff for: ios/MullvadVPN.xcodeproj/project.pbxproj

-4
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,6 @@
449449
58FF9FE42B075BDD00E4C97D /* EditAccessMethodItemIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FF9FE32B075BDD00E4C97D /* EditAccessMethodItemIdentifier.swift */; };
450450
58FF9FE82B07650A00E4C97D /* ButtonCellContentConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FF9FE72B07650A00E4C97D /* ButtonCellContentConfiguration.swift */; };
451451
58FF9FEA2B07653800E4C97D /* ButtonCellContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FF9FE92B07653800E4C97D /* ButtonCellContentView.swift */; };
452-
58FF9FEC2B07A7CB00E4C97D /* NSDirectionalEdgeInsets+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FF9FEB2B07A7CB00E4C97D /* NSDirectionalEdgeInsets+Helpers.swift */; };
453452
58FF9FF02B07C4D300E4C97D /* PersistentAccessMethod+ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FF9FEF2B07C4D300E4C97D /* PersistentAccessMethod+ViewModel.swift */; };
454453
58FF9FF42B07C61B00E4C97D /* AccessMethodValidationError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FF9FF32B07C61B00E4C97D /* AccessMethodValidationError.swift */; };
455454
7A02D4EB2A9CEC7A00C19E31 /* MullvadVPNScreenshots.xctestplan in Resources */ = {isa = PBXBuildFile; fileRef = 7A02D4EA2A9CEC7A00C19E31 /* MullvadVPNScreenshots.xctestplan */; };
@@ -1781,7 +1780,6 @@
17811780
58FF9FE32B075BDD00E4C97D /* EditAccessMethodItemIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditAccessMethodItemIdentifier.swift; sourceTree = "<group>"; };
17821781
58FF9FE72B07650A00E4C97D /* ButtonCellContentConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonCellContentConfiguration.swift; sourceTree = "<group>"; };
17831782
58FF9FE92B07653800E4C97D /* ButtonCellContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonCellContentView.swift; sourceTree = "<group>"; };
1784-
58FF9FEB2B07A7CB00E4C97D /* NSDirectionalEdgeInsets+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSDirectionalEdgeInsets+Helpers.swift"; sourceTree = "<group>"; };
17851783
58FF9FEF2B07C4D300E4C97D /* PersistentAccessMethod+ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PersistentAccessMethod+ViewModel.swift"; sourceTree = "<group>"; };
17861784
58FF9FF32B07C61B00E4C97D /* AccessMethodValidationError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessMethodValidationError.swift; sourceTree = "<group>"; };
17871785
7A02D4EA2A9CEC7A00C19E31 /* MullvadVPNScreenshots.xctestplan */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = MullvadVPNScreenshots.xctestplan; sourceTree = "<group>"; };
@@ -3010,7 +3008,6 @@
30103008
children = (
30113009
58CCA0152242560B004F3011 /* UIColor+Palette.swift */,
30123010
A9E034632ABB302000E59A5A /* UIEdgeInsets+Extensions.swift */,
3013-
58FF9FEB2B07A7CB00E4C97D /* NSDirectionalEdgeInsets+Helpers.swift */,
30143011
585CA70E25F8C44600B47C62 /* UIMetrics.swift */,
30153012
);
30163013
path = "UI appearance";
@@ -5832,7 +5829,6 @@
58325829
7A5869B92B56E7F000640D27 /* IPOverrideViewControllerDelegate.swift in Sources */,
58335830
586C0D7A2B039CE300E7CDD7 /* ShadowsocksCipherPicker.swift in Sources */,
58345831
58B93A1326C3F13600A55733 /* TunnelState.swift in Sources */,
5835-
58FF9FEC2B07A7CB00E4C97D /* NSDirectionalEdgeInsets+Helpers.swift in Sources */,
58365832
586C0D832B03D2FF00E7CDD7 /* ShadowsocksSectionHandler.swift in Sources */,
58375833
58B26E262943522400D5980C /* NotificationProvider.swift in Sources */,
58385834
58CE5E64224146200008646E /* AppDelegate.swift in Sources */,

Diff for: ios/MullvadVPN/Coordinators/Settings/APIAccess/Cells/ButtonCellContentView.swift

+1-2
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,7 @@ class ButtonCellContentView: UIView, UIContentView {
6666
button.titleLabel?.font = .systemFont(ofSize: 17)
6767
button.isEnabled = actualConfiguration.isEnabled
6868
button.style = actualConfiguration.style
69-
button.overrideContentEdgeInsets = true
70-
button.directionalContentEdgeInsets = actualConfiguration.directionalContentEdgeInsets
69+
button.configuration?.contentInsets = actualConfiguration.directionalContentEdgeInsets
7170
}
7271

7372
private func addSubviews() {

Diff for: ios/MullvadVPN/UI appearance/NSDirectionalEdgeInsets+Helpers.swift

-21
This file was deleted.

Diff for: ios/MullvadVPN/View controllers/Login/AccountInputGroupView.swift

+5-2
Original file line numberDiff line numberDiff line change
@@ -97,15 +97,18 @@ final class AccountInputGroupView: UIView {
9797
button.titleLabel?.font = accountNumberFont()
9898
button.setTitle(" ", for: .normal)
9999
button.contentHorizontalAlignment = .leading
100-
button.contentEdgeInsets = UIMetrics.textFieldMargins
101-
button.setTitleColor(UIColor.AccountTextField.NormalState.textColor, for: .normal)
102100
button.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
103101
button.accessibilityLabel = NSLocalizedString(
104102
"LAST_USED_ACCOUNT_ACCESSIBILITY_LABEL",
105103
tableName: "AccountInput",
106104
value: "Last used account",
107105
comment: ""
108106
)
107+
108+
var config = UIButton.Configuration.plain()
109+
config.contentInsets = UIMetrics.textFieldMargins.toDirectionalInsets
110+
config.baseForegroundColor = .AccountTextField.NormalState.textColor
111+
button.configuration = config
109112
return button
110113
}()
111114

Diff for: ios/MullvadVPN/View controllers/Tunnel/DisconnectSplitButton.swift

+17-8
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,6 @@ class DisconnectSplitButton: UIView {
3737
UIImage(named: "IconReload")?.imageFlippedForRightToLeftLayoutDirection(),
3838
for: .normal
3939
)
40-
41-
primaryButton.overrideContentEdgeInsets = true
4240
secondaryButtonWidthConstraint = secondaryButton.widthAnchor.constraint(equalToConstant: 0)
4341
secondaryButtonHeightConstraint = secondaryButton.heightAnchor
4442
.constraint(equalToConstant: 0)
@@ -72,18 +70,29 @@ class DisconnectSplitButton: UIView {
7270
}
7371

7472
private func adjustTitleLabelPosition() {
75-
var contentInsets = primaryButton.defaultContentInsets
73+
// Instead of setting contentEdgeInsets manually, we update UIButtonConfiguration
74+
var primaryButtonConfig = primaryButton.configuration ?? UIButton.Configuration.filled()
7675

7776
let offset = stackView.spacing + secondaryButtonSize.width
7877

78+
// Create content insets depending on layout direction
7979
if case .leftToRight = effectiveUserInterfaceLayoutDirection {
80-
contentInsets.left = offset
81-
contentInsets.right = 0
80+
primaryButtonConfig.contentInsets = NSDirectionalEdgeInsets(
81+
top: primaryButton.defaultContentInsets.top,
82+
leading: offset,
83+
bottom: primaryButton.defaultContentInsets.bottom,
84+
trailing: 0
85+
)
8286
} else {
83-
contentInsets.left = 0
84-
contentInsets.right = offset
87+
primaryButtonConfig.contentInsets = NSDirectionalEdgeInsets(
88+
top: primaryButton.defaultContentInsets.top,
89+
leading: 0,
90+
bottom: primaryButton.defaultContentInsets.bottom,
91+
trailing: offset
92+
)
8593
}
8694

87-
primaryButton.contentEdgeInsets = contentInsets
95+
// Apply updated configuration to the primary button
96+
primaryButton.configuration = primaryButtonConfig
8897
}
8998
}

Diff for: ios/MullvadVPN/Views/AppButton.swift

+22-47
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ import UIKit
1111
/// A subclass that implements action buttons used across the app
1212
class AppButton: CustomButton {
1313
/// Default content insets based on current trait collection.
14-
var defaultContentInsets: UIEdgeInsets {
14+
var defaultContentInsets: NSDirectionalEdgeInsets {
1515
switch traitCollection.userInterfaceIdiom {
1616
case .phone:
17-
return UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
17+
return NSDirectionalEdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10)
1818
case .pad:
19-
return UIEdgeInsets(top: 15, left: 15, bottom: 15, right: 15)
19+
return NSDirectionalEdgeInsets(top: 15, leading: 15, bottom: 15, trailing: 15)
2020
default:
2121
return .zero
2222
}
@@ -87,30 +87,6 @@ class AppButton: CustomButton {
8787
}
8888
}
8989

90-
/// Prevents updating `contentEdgeInsets` on changes to trait collection.
91-
var overrideContentEdgeInsets = false
92-
93-
override var contentEdgeInsets: UIEdgeInsets {
94-
didSet {
95-
// Reset inner directional insets when contentEdgeInsets are set directly.
96-
innerDirectionalContentEdgeInsets = nil
97-
}
98-
}
99-
100-
/// Directional content edge insets that are automatically translated into `contentEdgeInsets` immeditely upon updating the property and on trait collection
101-
/// changes.
102-
var directionalContentEdgeInsets: NSDirectionalEdgeInsets {
103-
get {
104-
innerDirectionalContentEdgeInsets ?? contentEdgeInsets.toDirectionalInsets
105-
}
106-
set {
107-
innerDirectionalContentEdgeInsets = newValue
108-
updateContentEdgeInsetsFromDirectional()
109-
}
110-
}
111-
112-
private var innerDirectionalContentEdgeInsets: NSDirectionalEdgeInsets?
113-
11490
init(style: Style) {
11591
self.style = style
11692
super.init(frame: .zero)
@@ -128,32 +104,31 @@ class AppButton: CustomButton {
128104
}
129105

130106
private func commonInit() {
131-
super.contentEdgeInsets = defaultContentInsets
132-
imageAlignment = .trailingFixed
133-
134-
titleLabel?.font = UIFont.systemFont(ofSize: 18, weight: .semibold)
135-
136-
let states: [UIControl.State] = [.normal, .highlighted, .disabled]
137-
states.forEach { state in
138-
if let titleColor = state.customButtonTitleColor {
139-
setTitleColor(titleColor, for: state)
107+
imageAlignment = .trailing
108+
titleAlignment = .leading
109+
110+
var config = super.configuration ?? .plain()
111+
config.title = title(for: state)
112+
config.contentInsets = defaultContentInsets
113+
config.background.image = style.backgroundImage
114+
config.titleTextAttributesTransformer =
115+
UIConfigurationTextAttributesTransformer { [weak self] attributeContainer in
116+
guard let self = self else { return attributeContainer }
117+
var updatedAttributeContainer = attributeContainer
118+
updatedAttributeContainer.font = UIFont.systemFont(ofSize: 18, weight: .semibold)
119+
updatedAttributeContainer.foregroundColor = self.state.customButtonTitleColor
120+
return updatedAttributeContainer
140121
}
141-
}
142122

143-
// Avoid setting the background image if it's already set via Interface Builder
144-
if backgroundImage(for: .normal) == nil {
145-
updateButtonBackground()
123+
let configurationHandler: UIButton.ConfigurationUpdateHandler = { button in
124+
button.alpha = button.state == .disabled ? 0.5 : 1.0
146125
}
126+
self.configuration = config
127+
self.configurationUpdateHandler = configurationHandler
147128
}
148129

149130
/// Set background image based on current style.
150131
private func updateButtonBackground() {
151-
setBackgroundImage(style.backgroundImage, for: .normal)
152-
}
153-
154-
/// Update content edge insets from directional edge insets if set.
155-
private func updateContentEdgeInsetsFromDirectional() {
156-
guard let directionalEdgeInsets = innerDirectionalContentEdgeInsets else { return }
157-
super.contentEdgeInsets = directionalEdgeInsets.toEdgeInsets(effectiveUserInterfaceLayoutDirection)
132+
self.configuration?.background.image = style.backgroundImage
158133
}
159134
}

0 commit comments

Comments
 (0)