9
9
import SwiftUI
10
10
11
11
//MARK: FloatingLabelTextField Style Protocol
12
- @available ( iOS 13 . 0 , * )
12
+ @available ( iOS 15 . 0 , * )
13
13
public protocol FloatingLabelTextFieldStyle {
14
14
func body( content: FloatingLabelTextField ) -> FloatingLabelTextField
15
15
}
16
16
17
17
//MARK: FloatingLabelTextField View
18
- @available ( iOS 13 . 0 , * )
18
+ @available ( iOS 15 . 0 , * )
19
19
public struct FloatingLabelTextField : View {
20
20
21
21
//MARK: Binding Property
@@ -36,20 +36,24 @@ public struct FloatingLabelTextField: View {
36
36
37
37
@State var isShowError : Bool = false
38
38
39
- @State fileprivate var isFocused : Bool = false
39
+ @FocusState fileprivate var isFocused : Bool
40
+ @State private var textFieldHeight : CGFloat = 0.0
40
41
41
42
//MARK: Observed Object
42
43
@ObservedObject private var notifier = FloatingLabelTextFieldNotifier ( )
43
44
44
45
//MARK: Properties
46
+ private let axis : Axis
45
47
private var placeholderText : String = " "
46
48
private var editingChanged : ( Bool ) -> ( ) = { _ in }
47
49
private var commit : ( ) -> ( ) = { }
48
50
49
51
//MARK: Init
50
- public init ( _ text: Binding < String > , validtionChecker: Binding < Bool > ? = nil , placeholder: String = " " , editingChanged: @escaping ( Bool ) -> ( ) = { _ in } , commit: @escaping ( ) -> ( ) = { } ) {
52
+ public init ( _ text: Binding < String > , validtionChecker: Binding < Bool > ? = nil , placeholder: String = " " ,
53
+ axis: Axis = . horizontal, editingChanged: @escaping ( Bool ) -> ( ) = { _ in } , commit: @escaping ( ) -> ( ) = { } ) {
51
54
self . _textFieldValue = text
52
55
self . placeholderText = placeholder
56
+ self . axis = axis
53
57
self . editingChanged = editingChanged
54
58
self . commit = commit
55
59
self . _validtionChecker = validtionChecker ?? Binding . constant ( false )
@@ -99,39 +103,81 @@ public struct FloatingLabelTextField: View {
99
103
. foregroundColor ( ( self . currentError. condition || !notifier. isShowError) ? ( isSelected ? notifier. selectedTextColor : notifier. textColor) : notifier. errorColor)
100
104
101
105
} else {
102
- TextField ( " " , text: $textFieldValue. animation ( ) , onEditingChanged: { ( isChanged) in
103
- withAnimation {
106
+ if #available( iOS 16 . 0 , * ) {
107
+ TextField ( " " , text: $textFieldValue. animation ( ) , axis: axis)
108
+ . focused ( $isFocused)
109
+ . onChange ( of: isFocused, perform: { ( isChanged) in
110
+ withAnimation {
111
+ DispatchQueue . main. async {
112
+ self . isSelected = isChanged
113
+ }
114
+ }
115
+
116
+ DispatchQueue . main. async {
117
+ self . isShowError = self . notifier. isRequiredField
118
+ }
119
+
120
+ self . validtionChecker = self . currentError. condition
121
+ self . editingChanged ( isChanged)
122
+ arrTextFieldEditActions = self . notifier. arrTextFieldEditActions
123
+ } )
124
+ . onSubmit ( {
125
+ self . isShowError = self . notifier. isRequiredField
126
+ self . validtionChecker = self . currentError. condition
127
+ self . commit ( )
128
+ arrTextFieldEditActions = [ ]
129
+ } )
130
+ . lineLimit ( notifier. lineLimit)
131
+ . disabled ( self . notifier. disabled)
132
+ . allowsHitTesting ( self . notifier. allowsHitTesting)
133
+ . multilineTextAlignment ( notifier. textAlignment)
134
+ . font ( notifier. font)
135
+ . foregroundColor ( ( self . currentError. condition || !notifier. isShowError) ? ( isSelected ? notifier. selectedTextColor : notifier. textColor) : notifier. errorColor)
136
+ . background (
137
+ GeometryReader ( content: set ( geometry: ) )
138
+ )
139
+ } else {
140
+ TextField ( " " , text: $textFieldValue. animation ( ) , onEditingChanged: { ( isChanged) in
141
+ withAnimation {
142
+ DispatchQueue . main. async {
143
+ self . isSelected = isChanged
144
+ }
145
+ }
146
+
104
147
DispatchQueue . main. async {
105
- self . isSelected = isChanged
148
+ self . isShowError = self . notifier . isRequiredField
106
149
}
107
- }
108
150
109
- DispatchQueue . main. async {
151
+ self . validtionChecker = self . currentError. condition
152
+ self . editingChanged ( isChanged)
153
+ arrTextFieldEditActions = self . notifier. arrTextFieldEditActions
154
+ } , onCommit: {
110
155
self . isShowError = self . notifier. isRequiredField
111
- }
112
-
113
- self . validtionChecker = self . currentError. condition
114
- self . editingChanged ( isChanged)
115
- arrTextFieldEditActions = self . notifier. arrTextFieldEditActions
116
- } , onCommit: {
117
- self . isShowError = self . notifier. isRequiredField
118
- self . validtionChecker = self . currentError. condition
119
- self . commit ( )
120
- arrTextFieldEditActions = [ ]
121
- } )
122
- . disabled ( self . notifier. disabled)
123
- . allowsHitTesting ( self . notifier. allowsHitTesting)
124
- . multilineTextAlignment ( notifier. textAlignment)
125
- . font ( notifier. font)
126
- . foregroundColor ( ( self . currentError. condition || !notifier. isShowError) ? ( isSelected ? notifier. selectedTextColor : notifier. textColor) : notifier. errorColor)
156
+ self . validtionChecker = self . currentError. condition
157
+ self . commit ( )
158
+ arrTextFieldEditActions = [ ]
159
+ } )
160
+ . disabled ( self . notifier. disabled)
161
+ . allowsHitTesting ( self . notifier. allowsHitTesting)
162
+ . multilineTextAlignment ( notifier. textAlignment)
163
+ . font ( notifier. font)
164
+ . foregroundColor ( ( self . currentError. condition || !notifier. isShowError) ? ( isSelected ? notifier. selectedTextColor : notifier. textColor) : notifier. errorColor)
165
+ }
127
166
}
128
167
}
129
168
}
130
169
170
+ private func set( geometry: GeometryProxy ) -> some View {
171
+ DispatchQueue . main. async {
172
+ self . textFieldHeight = geometry. size. height
173
+ }
174
+ return Color . clear
175
+ }
176
+
131
177
// MARK: Top error and title lable view
132
178
var topTitleLable : some View {
133
179
Text ( ( self . currentError. condition || !notifier. isShowError) ? placeholderText : self . currentError. errorMessage)
134
- . frame ( minWidth : 0 , maxWidth : . infinity , minHeight : 0 , maxHeight : . infinity , alignment: notifier. textAlignment. getAlignment ( ) )
180
+ . frame ( alignment: notifier. textAlignment. getAlignment ( ) )
135
181
. animation ( . default)
136
182
. foregroundColor ( ( self . currentError. condition || !notifier. isShowError) ? ( self . isSelected ? notifier. selectedTitleColor : notifier. titleColor) : notifier. errorColor)
137
183
. font ( notifier. titleFont)
@@ -153,7 +199,9 @@ public struct FloatingLabelTextField: View {
153
199
self . topTitleLable. padding ( . bottom, CGFloat ( notifier. spaceBetweenTitleText) ) . opacity ( 1 )
154
200
155
201
} else {
156
- self . topTitleLable. padding ( . bottom, CGFloat ( !textFieldValue. isEmpty ? notifier. spaceBetweenTitleText : 0 ) ) . opacity ( ( textFieldValue. isEmpty) ? 0 : 1 )
202
+ let padding = notifier. spaceBetweenTitleText + ( axis == . vertical ? textFieldHeight : 0 )
203
+ self . topTitleLable. padding ( . bottom, !textFieldValue. isEmpty ? padding : 0 )
204
+ . opacity ( ( textFieldValue. isEmpty) ? 0 : 1 )
157
205
}
158
206
159
207
HStack {
@@ -179,20 +227,20 @@ public struct FloatingLabelTextField: View {
179
227
}
180
228
181
229
}
182
- . frame ( minWidth : 0 , maxWidth : . infinity , minHeight : 0 , maxHeight : . infinity , alignment: . bottomLeading)
230
+ . frame ( alignment: . bottomLeading)
183
231
}
184
232
}
185
233
186
234
//MARK: FloatingLabelTextField Style Funcation
187
- @available ( iOS 13 . 0 , * )
235
+ @available ( iOS 15 . 0 , * )
188
236
extension FloatingLabelTextField {
189
237
public func floatingStyle< S> ( _ style: S ) -> some View where S: FloatingLabelTextFieldStyle {
190
238
return style. body ( content: self )
191
239
}
192
240
}
193
241
194
242
//MARK: View Property Funcation
195
- @available ( iOS 13 . 0 , * )
243
+ @available ( iOS 15 . 0 , * )
196
244
extension FloatingLabelTextField {
197
245
/// Sets the left view.
198
246
public func leftView< LRView: View > ( @ViewBuilder _ view: @escaping ( ) -> LRView ) -> Self {
@@ -208,7 +256,7 @@ extension FloatingLabelTextField {
208
256
}
209
257
210
258
//MARK: Text Property Funcation
211
- @available ( iOS 13 . 0 , * )
259
+ @available ( iOS 15 . 0 , * )
212
260
extension FloatingLabelTextField {
213
261
/// Sets the alignment for text.
214
262
public func textAlignment( _ alignment: TextAlignment ) -> Self {
@@ -235,8 +283,16 @@ extension FloatingLabelTextField {
235
283
}
236
284
}
237
285
286
+ @available ( iOS 16 . 0 , * )
287
+ extension FloatingLabelTextField {
288
+ public func lineLimit( _ limit: Int ) -> Self {
289
+ notifier. lineLimit = limit
290
+ return self
291
+ }
292
+ }
293
+
238
294
//MARK: Line Property Funcation
239
- @available ( iOS 13 . 0 , * )
295
+ @available ( iOS 15 . 0 , * )
240
296
extension FloatingLabelTextField {
241
297
/// Sets the line height.
242
298
public func lineHeight( _ height: CGFloat ) -> Self {
@@ -264,7 +320,7 @@ extension FloatingLabelTextField {
264
320
}
265
321
266
322
//MARK: Title Property Funcation
267
- @available ( iOS 13 . 0 , * )
323
+ @available ( iOS 15 . 0 , * )
268
324
extension FloatingLabelTextField {
269
325
/// Sets the title color.
270
326
public func titleColor( _ color: Color ) -> Self {
@@ -292,7 +348,7 @@ extension FloatingLabelTextField {
292
348
}
293
349
294
350
//MARK: Text Property Funcation
295
- @available ( iOS 13 . 0 , * )
351
+ @available ( iOS 15 . 0 , * )
296
352
extension FloatingLabelTextField {
297
353
/// Sets the text color.
298
354
public func textColor( _ color: Color ) -> Self {
@@ -314,7 +370,7 @@ extension FloatingLabelTextField {
314
370
}
315
371
316
372
//MARK: Placeholder Property Funcation
317
- @available ( iOS 13 . 0 , * )
373
+ @available ( iOS 15 . 0 , * )
318
374
extension FloatingLabelTextField {
319
375
/// Sets the placeholder color.
320
376
public func placeholderColor( _ color: Color ) -> Self {
@@ -330,7 +386,7 @@ extension FloatingLabelTextField {
330
386
}
331
387
332
388
//MARK: Error Property Funcation
333
- @available ( iOS 13 . 0 , * )
389
+ @available ( iOS 15 . 0 , * )
334
390
extension FloatingLabelTextField {
335
391
/// Sets the is show error message.
336
392
public func isShowError( _ show: Bool ) -> Self {
@@ -365,7 +421,7 @@ extension FloatingLabelTextField {
365
421
}
366
422
367
423
//MARK: Text Field Editing Funcation
368
- @available ( iOS 13 . 0 , * )
424
+ @available ( iOS 15 . 0 , * )
369
425
extension FloatingLabelTextField {
370
426
/// Disable text field editing action. Like cut, copy, past, all etc.
371
427
public func addDisableEditingAction( _ actions: [ TextFieldEditActions ] ) -> Self {
@@ -375,7 +431,7 @@ extension FloatingLabelTextField {
375
431
}
376
432
377
433
//MARK: Animation Style Funcation
378
- @available ( iOS 13 . 0 , * )
434
+ @available ( iOS 15 . 0 , * )
379
435
extension FloatingLabelTextField {
380
436
/// Enable the placeholder label when the textfield is focused.
381
437
public func enablePlaceholderOnFocus( _ isEanble: Bool ) -> Self {
0 commit comments