Skip to content

Commit 0a9ca09

Browse files
update UKInputField to support titlePosition
1 parent 0a1554b commit 0a9ca09

File tree

3 files changed

+85
-50
lines changed

3 files changed

+85
-50
lines changed

Examples/DemosApp/DemosApp/ComponentsPreview/PreviewPages/InputFieldPreview.swift

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ struct InputFieldPreview: View {
4646
return self.model.placeholder != nil
4747
},
4848
set: { newValue in
49-
self.model.placeholder = newValue ? "Placeholder" : nil
49+
self.model.placeholder = newValue ? Self.placeholder : nil
5050
}
5151
))
5252
Toggle("Required", isOn: self.$model.isRequired)
@@ -66,7 +66,7 @@ struct InputFieldPreview: View {
6666
return self.model.title != nil
6767
},
6868
set: { newValue in
69-
self.model.title = newValue ? "Title" : nil
69+
self.model.title = newValue ? Self.title : nil
7070
}
7171
))
7272
Picker("Title Position", selection: self.$model.titlePosition) {
@@ -87,9 +87,12 @@ struct InputFieldPreview: View {
8787
}
8888
}
8989

90+
private static let title = "Title"
91+
private static let placeholder = "Placeholder"
9092
private static var initialModel: InputFieldVM {
9193
return .init {
92-
$0.title = "Title"
94+
$0.title = Self.title
95+
$0.placeholder = Self.placeholder
9396
}
9497
}
9598
}

Sources/ComponentsKit/Components/InputField/Models/InputFieldVM.swift

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -108,10 +108,6 @@ extension InputFieldVM {
108108
}
109109
}
110110
var spacing: CGFloat {
111-
guard self.title.isNotNilAndEmpty else {
112-
return 0
113-
}
114-
115111
switch self.titlePosition {
116112
case .inside:
117113
return 12
@@ -203,14 +199,16 @@ extension InputFieldVM {
203199
}
204200
return attributedString
205201
}
202+
func shouldUpdateTitlePosition(_ oldModel: Self) -> Bool {
203+
return self.titlePosition != oldModel.titlePosition
204+
}
206205
func shouldUpdateLayout(_ oldModel: Self) -> Bool {
207206
return self.size != oldModel.size
208207
|| self.horizontalPadding != oldModel.horizontalPadding
209208
|| self.spacing != oldModel.spacing
210209
|| self.cornerRadius != oldModel.cornerRadius
211-
}
212-
func shouldUpdateCornerRadius(_ oldModel: Self) -> Bool {
213-
return self.cornerRadius != oldModel.cornerRadius
210+
|| self.titlePosition != oldModel.titlePosition
211+
|| self.title.isNilOrEmpty != oldModel.title.isNilOrEmpty
214212
}
215213
}
216214

Sources/ComponentsKit/Components/InputField/UKInputField.swift

Lines changed: 74 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import UIKit
33

44
/// A UIKit component that displays a field to input a text.
55
open class UKInputField: UIView, UKComponent {
6-
// MARK: Properties
6+
// MARK: Public Properties
77

88
/// A closure that is triggered when the text changes.
99
public var onValueChange: (String) -> Void
@@ -28,15 +28,23 @@ open class UKInputField: UIView, UKComponent {
2828
}
2929
}
3030

31-
private var titleLabelConstraints: LayoutConstraints?
32-
private var inputFieldConstraints: LayoutConstraints?
33-
3431
// MARK: Subviews
3532

3633
/// A label that displays the title from the model.
3734
public var titleLabel = UILabel()
3835
/// An underlying text field from the standard library.
3936
public var textField = UITextField()
37+
/// A view that contains `horizontalStackView` and `titleLabel` whet it is outside.
38+
public var textFieldContainer = UIView()
39+
/// A stack view that contains `textField` and `titleLabel` whet it is inside.
40+
public var horizontalStackView = UIStackView()
41+
/// A stack view that contains `textFieldContainer`.
42+
public var verticalStackView = UIStackView()
43+
44+
// MARK: Private Properties
45+
46+
private var textFieldContainerConstraints = LayoutConstraints()
47+
private var horizontalStackViewConstraints = LayoutConstraints()
4048

4149
// MARK: UIView Properties
4250

@@ -78,10 +86,18 @@ open class UKInputField: UIView, UKComponent {
7886
// MARK: Setup
7987

8088
private func setup() {
81-
self.addSubview(self.titleLabel)
82-
self.addSubview(self.textField)
89+
self.addSubview(self.verticalStackView)
90+
switch self.model.titlePosition {
91+
case .inside:
92+
self.horizontalStackView.addArrangedSubview(self.titleLabel)
93+
case .outside:
94+
self.verticalStackView.addArrangedSubview(self.titleLabel)
95+
}
96+
self.verticalStackView.addArrangedSubview(self.textFieldContainer)
97+
self.horizontalStackView.addArrangedSubview(self.textField)
98+
self.textFieldContainer.addSubview(self.horizontalStackView)
8399

84-
self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.handleTap)))
100+
self.textFieldContainer.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.handleTap)))
85101
self.textField.addTarget(self, action: #selector(self.handleTextChange), for: .editingChanged)
86102

87103
if #available(iOS 17.0, *) {
@@ -102,46 +118,48 @@ open class UKInputField: UIView, UKComponent {
102118
// MARK: Style
103119

104120
private func style() {
105-
Self.Style.mainView(self, model: self.model)
121+
Self.Style.textFieldContainer(self.textFieldContainer, model: self.model)
122+
Self.Style.horizontalStackView(self.horizontalStackView, model: self.model)
123+
Self.Style.verticalStackView(self.verticalStackView, model: self.model)
106124
Self.Style.textField(self.textField, model: self.model)
107125
Self.Style.titleLabel(self.titleLabel, model: self.model)
108126
}
109127

110128
// MARK: Layout
111129

112130
private func layout() {
113-
self.titleLabelConstraints = self.titleLabel.leading(self.model.horizontalPadding)
114-
self.titleLabel.centerVertically()
131+
self.verticalStackView.allEdges()
115132

116-
self.textField.trailing(self.model.horizontalPadding)
117-
self.textField.vertically()
133+
self.textFieldContainerConstraints = self.textFieldContainer.height(self.model.height)
134+
self.textFieldContainer.horizontally()
118135

119-
self.inputFieldConstraints = self.textField.after(
120-
self.titleLabel,
121-
padding: self.model.spacing
122-
)
136+
self.horizontalStackView.vertically()
137+
self.horizontalStackViewConstraints = self.horizontalStackView.horizontally(self.model.horizontalPadding)
123138

124139
self.textField.setContentHuggingPriority(.defaultLow, for: .horizontal)
125140
self.titleLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal)
126141
}
127142

128-
open override func layoutSubviews() {
129-
super.layoutSubviews()
130-
131-
self.updateCornerRadius()
132-
}
133-
134143
// MARK: Update
135144

136145
public func update(_ oldModel: InputFieldVM) {
137146
guard self.model != oldModel else { return }
138147

139148
self.style()
140149

141-
self.inputFieldConstraints?.leading?.constant = self.model.spacing
142-
self.titleLabelConstraints?.leading?.constant = self.model.horizontalPadding
143-
if self.model.shouldUpdateCornerRadius(oldModel) {
144-
self.updateCornerRadius()
150+
self.horizontalStackViewConstraints.leading?.constant = self.model.horizontalPadding
151+
self.horizontalStackViewConstraints.trailing?.constant = -self.model.horizontalPadding
152+
self.textFieldContainerConstraints.height?.constant = self.model.height
153+
154+
if self.model.shouldUpdateTitlePosition(oldModel) {
155+
switch self.model.titlePosition {
156+
case .inside:
157+
self.verticalStackView.removeArrangedSubview(self.titleLabel)
158+
self.horizontalStackView.insertArrangedSubview(self.titleLabel, at: 0)
159+
case .outside:
160+
self.horizontalStackView.removeArrangedSubview(self.titleLabel)
161+
self.verticalStackView.insertArrangedSubview(self.titleLabel, at: 0)
162+
}
145163
}
146164
if self.model.shouldUpdateLayout(oldModel) {
147165
self.setNeedsLayout()
@@ -169,52 +187,52 @@ open class UKInputField: UIView, UKComponent {
169187
} else {
170188
width = 10_000
171189
}
190+
191+
let height = self.verticalStackView.sizeThatFits(UIView.layoutFittingCompressedSize).height
192+
172193
return .init(
173194
width: min(size.width, width),
174-
height: min(size.height, self.model.height)
195+
height: min(size.height, height)
175196
)
176197
}
177-
198+
178199
open override func traitCollectionDidChange(
179200
_ previousTraitCollection: UITraitCollection?
180201
) {
181202
super.traitCollectionDidChange(previousTraitCollection)
182203
self.handleTraitChanges()
183204
}
184-
205+
185206
// MARK: Helpers
186-
187-
@objc private func handleTraitChanges() {
188-
Self.Style.mainView(self, model: self.model)
189-
}
190207

191-
private func updateCornerRadius() {
192-
self.layer.cornerRadius = self.model.cornerRadius.value(for: self.bounds.height)
208+
@objc private func handleTraitChanges() {
209+
Self.Style.textFieldContainer(self.textFieldContainer, model: self.model)
193210
}
194211
}
195212

196213
// MARK: - Style Helpers
197214

198215
extension UKInputField {
199216
fileprivate enum Style {
200-
static func mainView(
217+
static func textFieldContainer(
201218
_ view: UIView,
202-
model: InputFieldVM
219+
model: Model
203220
) {
204221
view.backgroundColor = model.backgroundColor.uiColor
205-
view.layer.cornerRadius = model.cornerRadius.value(for: view.bounds.height)
222+
view.layer.cornerRadius = model.cornerRadius.value(for: model.height)
206223
view.layer.borderWidth = model.borderWidth
207224
view.layer.borderColor = model.borderColor.cgColor
208225
}
209226
static func titleLabel(
210227
_ label: UILabel,
211-
model: InputFieldVM
228+
model: Model
212229
) {
213230
label.attributedText = model.nsAttributedTitle
231+
label.isVisible = model.title.isNotNilAndEmpty
214232
}
215233
static func textField(
216234
_ textField: UITextField,
217-
model: InputFieldVM
235+
model: Model
218236
) {
219237
textField.font = model.preferredFont.uiFont
220238
textField.textColor = model.foregroundColor.uiColor
@@ -227,5 +245,21 @@ extension UKInputField {
227245
textField.autocorrectionType = model.autocorrectionType
228246
textField.autocapitalizationType = model.autocapitalization.textAutocapitalizationType
229247
}
248+
static func horizontalStackView(
249+
_ stackView: UIStackView,
250+
model: Model
251+
) {
252+
stackView.axis = .horizontal
253+
stackView.spacing = model.spacing
254+
}
255+
static func verticalStackView(
256+
_ stackView: UIStackView,
257+
model: Model
258+
) {
259+
stackView.axis = .vertical
260+
stackView.spacing = model.spacing
261+
stackView.alignment = .leading
262+
stackView.distribution = .fillProportionally
263+
}
230264
}
231265
}

0 commit comments

Comments
 (0)