Skip to content

Commit 9e5d302

Browse files
committed
improve UKButton
1 parent f3949d5 commit 9e5d302

File tree

3 files changed

+91
-11
lines changed

3 files changed

+91
-11
lines changed

Examples/DemosApp/DemosApp/ComponentsPreview/PreviewPages/ButtonPreview.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ struct ButtonPreview: View {
2323
Text("Custom: 20px").tag(ComponentRadius.custom(20))
2424
}
2525
ButtonFontPicker(selection: self.$model.font)
26-
Toggle("Enabled", isOn: self.$model.isEnabled)
26+
if !self.model.isLoading {
27+
Toggle("Enabled", isOn: self.$model.isEnabled)
28+
}
2729
Toggle("Full Width", isOn: self.$model.isFullWidth)
2830
Picker("Image Source", selection: self.$model.imageSrc) {
2931
Text("SF Symbol").tag(ButtonVM.ImageSource.sfSymbol("camera.fill"))

Sources/ComponentsKit/Components/Button/Models/ButtonVM.swift

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ public struct ButtonVM: ComponentVM {
7474
// MARK: Shared Helpers
7575

7676
extension ButtonVM {
77+
private var isInteractive: Bool {
78+
self.isEnabled && !self.isLoading
79+
}
80+
7781
var preferredLoadingVM: LoadingVM {
7882
return self.loadingVM ?? .init {
7983
$0.color = .init(
@@ -87,10 +91,10 @@ extension ButtonVM {
8791
switch self.style {
8892
case .filled:
8993
let color = self.color?.main ?? .content2
90-
return color.enabled(self.isEnabled)
94+
return color.enabled(self.isInteractive)
9195
case .light:
9296
let color = self.color?.background ?? .content1
93-
return color.enabled(self.isEnabled)
97+
return color.enabled(self.isInteractive)
9498
case .plain, .bordered:
9599
return nil
96100
}
@@ -102,7 +106,7 @@ extension ButtonVM {
102106
case .plain, .light, .bordered:
103107
self.color?.main ?? .foreground
104108
}
105-
return color.enabled(self.isEnabled)
109+
return color.enabled(self.isInteractive)
106110
}
107111
var borderWidth: CGFloat {
108112
switch self.style {
@@ -118,7 +122,7 @@ extension ButtonVM {
118122
return nil
119123
case .bordered:
120124
if let color {
121-
return color.main.enabled(self.isEnabled)
125+
return color.main.enabled(self.isInteractive)
122126
} else {
123127
return .divider
124128
}
@@ -193,6 +197,18 @@ extension ButtonVM {
193197
}
194198
}
195199

200+
extension ButtonVM {
201+
public var uiImage: UIImage? {
202+
guard let imageSrc = self.imageSrc else { return nil }
203+
switch imageSrc {
204+
case .sfSymbol(let name):
205+
return UIImage(systemName: name)?.withRenderingMode(.alwaysTemplate)
206+
case .local(let name, let bundle):
207+
return UIImage(named: name, in: bundle, compatibleWith: nil)?.withRenderingMode(.alwaysTemplate)
208+
}
209+
}
210+
}
211+
196212
// MARK: SwiftUI Helpers
197213

198214
extension ButtonVM {
@@ -206,9 +222,9 @@ extension ButtonVM {
206222
guard let imageSrc = self.imageSrc else { return nil }
207223
switch imageSrc {
208224
case .sfSymbol(let name):
209-
return Image(systemName: name)
225+
return Image(systemName: name).renderingMode(.template)
210226
case .local(let name, let bundle):
211-
return Image(name, bundle: bundle)
227+
return Image(name, bundle: bundle).renderingMode(.template)
212228
}
213229
}
214230
}

Sources/ComponentsKit/Components/Button/UKButton.swift

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,15 @@ open class UKButton: UIView, UKComponent {
3232
/// A label that displays the title from the model.
3333
public var titleLabel = UILabel()
3434

35+
/// A loader view, created with the preferred loading VM from the model.
36+
public let loaderView: UKLoading
37+
38+
/// A stack view that arranges the loader and title label.
39+
private let stackView = UIStackView()
40+
41+
/// A image view for displaying the image from the model.
42+
public let imageView: UIImageView = UIImageView()
43+
3544
// MARK: UIView Properties
3645

3746
open override var intrinsicContentSize: CGSize {
@@ -50,6 +59,7 @@ open class UKButton: UIView, UKComponent {
5059
) {
5160
self.model = model
5261
self.action = action
62+
self.loaderView = UKLoading(model: model.preferredLoadingVM)
5363
super.init(frame: .zero)
5464

5565
self.setup()
@@ -64,7 +74,7 @@ open class UKButton: UIView, UKComponent {
6474
// MARK: Setup
6575

6676
private func setup() {
67-
self.addSubview(self.titleLabel)
77+
self.addSubview(self.stackView)
6878

6979
if #available(iOS 17.0, *) {
7080
self.registerForTraitChanges([UITraitUserInterfaceStyle.self]) { (view: Self, _: UITraitCollection) in
@@ -78,12 +88,23 @@ open class UKButton: UIView, UKComponent {
7888
private func style() {
7989
Self.Style.mainView(self, model: self.model)
8090
Self.Style.titleLabel(self.titleLabel, model: self.model)
91+
Self.Style.configureStackView(
92+
self.stackView,
93+
model: self.model,
94+
loaderView: self.loaderView,
95+
titleLabel: self.titleLabel,
96+
imageView: self.imageView
97+
)
98+
99+
self.loaderView.model = self.model.preferredLoadingVM
100+
101+
self.loaderView.isHidden = !self.model.isLoading
81102
}
82103

83104
// MARK: Layout
84105

85106
private func layout() {
86-
self.titleLabel.center()
107+
self.stackView.center()
87108
}
88109

89110
open override func layoutSubviews() {
@@ -99,15 +120,21 @@ open class UKButton: UIView, UKComponent {
99120

100121
self.style()
101122

102-
if self.model.shouldUpdateSize(oldModel) {
123+
self.imageView.image = self.model.uiImage
124+
self.imageView.tintColor = self.model.foregroundColor.uiColor
125+
126+
if self.model.shouldUpdateSize(oldModel)
127+
|| self.model.isLoading != oldModel.isLoading
128+
|| self.model.imageSrc != oldModel.imageSrc
129+
|| self.model.imageLocation != oldModel.imageLocation {
103130
self.invalidateIntrinsicContentSize()
104131
}
105132
}
106133

107134
// MARK: UIView methods
108135

109136
open override func sizeThatFits(_ size: CGSize) -> CGSize {
110-
let contentSize = self.titleLabel.sizeThatFits(size)
137+
let contentSize = self.stackView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
111138
let preferredSize = self.model.preferredSize(
112139
for: contentSize,
113140
parentWidth: self.superview?.bounds.width
@@ -183,5 +210,40 @@ extension UKButton {
183210
label.font = model.preferredFont.uiFont
184211
label.textColor = model.foregroundColor.uiColor
185212
}
213+
static func configureStackView(
214+
_ stackView: UIStackView,
215+
model: Model,
216+
loaderView: UKLoading,
217+
titleLabel: UILabel,
218+
imageView: UIImageView
219+
) {
220+
stackView.axis = .horizontal
221+
stackView.alignment = .center
222+
stackView.spacing = model.contentSpacing
223+
224+
for subview in stackView.arrangedSubviews {
225+
stackView.removeArrangedSubview(subview)
226+
subview.removeFromSuperview()
227+
}
228+
229+
if model.isLoading {
230+
stackView.addArrangedSubview(loaderView)
231+
stackView.addArrangedSubview(titleLabel)
232+
return
233+
}
234+
235+
if let _ = model.imageSrc {
236+
switch model.imageLocation {
237+
case .leading:
238+
stackView.addArrangedSubview(imageView)
239+
stackView.addArrangedSubview(titleLabel)
240+
case .trailing:
241+
stackView.addArrangedSubview(titleLabel)
242+
stackView.addArrangedSubview(imageView)
243+
}
244+
} else {
245+
stackView.addArrangedSubview(titleLabel)
246+
}
247+
}
186248
}
187249
}

0 commit comments

Comments
 (0)