@@ -3,7 +3,7 @@ import UIKit
33
44/// A UIKit component that displays a field to input a text.
55open 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
198215extension 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