99import SwiftUI
1010
1111//MARK: FloatingLabelTextField Style Protocol
12- @available ( iOS 13 . 0 , * )
12+ @available ( iOS 15 . 0 , * )
1313public protocol FloatingLabelTextFieldStyle {
1414 func body( content: FloatingLabelTextField ) -> FloatingLabelTextField
1515}
1616
1717//MARK: FloatingLabelTextField View
18- @available ( iOS 13 . 0 , * )
18+ @available ( iOS 15 . 0 , * )
1919public struct FloatingLabelTextField : View {
2020
2121 //MARK: Binding Property
@@ -36,20 +36,24 @@ public struct FloatingLabelTextField: View {
3636
3737 @State var isShowError : Bool = false
3838
39- @State fileprivate var isFocused : Bool = false
39+ @FocusState fileprivate var isFocused : Bool
40+ @State private var textFieldHeight : CGFloat = 0.0
4041
4142 //MARK: Observed Object
4243 @ObservedObject private var notifier = FloatingLabelTextFieldNotifier ( )
4344
4445 //MARK: Properties
46+ private let axis : Axis
4547 private var placeholderText : String = " "
4648 private var editingChanged : ( Bool ) -> ( ) = { _ in }
4749 private var commit : ( ) -> ( ) = { }
4850
4951 //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 ( ) -> ( ) = { } ) {
5154 self . _textFieldValue = text
5255 self . placeholderText = placeholder
56+ self . axis = axis
5357 self . editingChanged = editingChanged
5458 self . commit = commit
5559 self . _validtionChecker = validtionChecker ?? Binding . constant ( false )
@@ -99,39 +103,81 @@ public struct FloatingLabelTextField: View {
99103 . foregroundColor ( ( self . currentError. condition || !notifier. isShowError) ? ( isSelected ? notifier. selectedTextColor : notifier. textColor) : notifier. errorColor)
100104
101105 } 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+
104147 DispatchQueue . main. async {
105- self . isSelected = isChanged
148+ self . isShowError = self . notifier . isRequiredField
106149 }
107- }
108150
109- DispatchQueue . main. async {
151+ self . validtionChecker = self . currentError. condition
152+ self . editingChanged ( isChanged)
153+ arrTextFieldEditActions = self . notifier. arrTextFieldEditActions
154+ } , onCommit: {
110155 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+ }
127166 }
128167 }
129168 }
130169
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+
131177 // MARK: Top error and title lable view
132178 var topTitleLable : some View {
133179 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 ( ) )
135181 . animation ( . default)
136182 . foregroundColor ( ( self . currentError. condition || !notifier. isShowError) ? ( self . isSelected ? notifier. selectedTitleColor : notifier. titleColor) : notifier. errorColor)
137183 . font ( notifier. titleFont)
@@ -153,7 +199,9 @@ public struct FloatingLabelTextField: View {
153199 self . topTitleLable. padding ( . bottom, CGFloat ( notifier. spaceBetweenTitleText) ) . opacity ( 1 )
154200
155201 } 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 )
157205 }
158206
159207 HStack {
@@ -179,20 +227,20 @@ public struct FloatingLabelTextField: View {
179227 }
180228
181229 }
182- . frame ( minWidth : 0 , maxWidth : . infinity , minHeight : 0 , maxHeight : . infinity , alignment: . bottomLeading)
230+ . frame ( alignment: . bottomLeading)
183231 }
184232}
185233
186234//MARK: FloatingLabelTextField Style Funcation
187- @available ( iOS 13 . 0 , * )
235+ @available ( iOS 15 . 0 , * )
188236extension FloatingLabelTextField {
189237 public func floatingStyle< S> ( _ style: S ) -> some View where S: FloatingLabelTextFieldStyle {
190238 return style. body ( content: self )
191239 }
192240}
193241
194242//MARK: View Property Funcation
195- @available ( iOS 13 . 0 , * )
243+ @available ( iOS 15 . 0 , * )
196244extension FloatingLabelTextField {
197245 /// Sets the left view.
198246 public func leftView< LRView: View > ( @ViewBuilder _ view: @escaping ( ) -> LRView ) -> Self {
@@ -208,7 +256,7 @@ extension FloatingLabelTextField {
208256}
209257
210258//MARK: Text Property Funcation
211- @available ( iOS 13 . 0 , * )
259+ @available ( iOS 15 . 0 , * )
212260extension FloatingLabelTextField {
213261 /// Sets the alignment for text.
214262 public func textAlignment( _ alignment: TextAlignment ) -> Self {
@@ -235,8 +283,16 @@ extension FloatingLabelTextField {
235283 }
236284}
237285
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+
238294//MARK: Line Property Funcation
239- @available ( iOS 13 . 0 , * )
295+ @available ( iOS 15 . 0 , * )
240296extension FloatingLabelTextField {
241297 /// Sets the line height.
242298 public func lineHeight( _ height: CGFloat ) -> Self {
@@ -264,7 +320,7 @@ extension FloatingLabelTextField {
264320}
265321
266322//MARK: Title Property Funcation
267- @available ( iOS 13 . 0 , * )
323+ @available ( iOS 15 . 0 , * )
268324extension FloatingLabelTextField {
269325 /// Sets the title color.
270326 public func titleColor( _ color: Color ) -> Self {
@@ -292,7 +348,7 @@ extension FloatingLabelTextField {
292348}
293349
294350//MARK: Text Property Funcation
295- @available ( iOS 13 . 0 , * )
351+ @available ( iOS 15 . 0 , * )
296352extension FloatingLabelTextField {
297353 /// Sets the text color.
298354 public func textColor( _ color: Color ) -> Self {
@@ -314,7 +370,7 @@ extension FloatingLabelTextField {
314370}
315371
316372//MARK: Placeholder Property Funcation
317- @available ( iOS 13 . 0 , * )
373+ @available ( iOS 15 . 0 , * )
318374extension FloatingLabelTextField {
319375 /// Sets the placeholder color.
320376 public func placeholderColor( _ color: Color ) -> Self {
@@ -330,7 +386,7 @@ extension FloatingLabelTextField {
330386}
331387
332388//MARK: Error Property Funcation
333- @available ( iOS 13 . 0 , * )
389+ @available ( iOS 15 . 0 , * )
334390extension FloatingLabelTextField {
335391 /// Sets the is show error message.
336392 public func isShowError( _ show: Bool ) -> Self {
@@ -365,7 +421,7 @@ extension FloatingLabelTextField {
365421}
366422
367423//MARK: Text Field Editing Funcation
368- @available ( iOS 13 . 0 , * )
424+ @available ( iOS 15 . 0 , * )
369425extension FloatingLabelTextField {
370426 /// Disable text field editing action. Like cut, copy, past, all etc.
371427 public func addDisableEditingAction( _ actions: [ TextFieldEditActions ] ) -> Self {
@@ -375,7 +431,7 @@ extension FloatingLabelTextField {
375431}
376432
377433//MARK: Animation Style Funcation
378- @available ( iOS 13 . 0 , * )
434+ @available ( iOS 15 . 0 , * )
379435extension FloatingLabelTextField {
380436 /// Enable the placeholder label when the textfield is focused.
381437 public func enablePlaceholderOnFocus( _ isEanble: Bool ) -> Self {
0 commit comments