@@ -150,13 +150,58 @@ public struct FirstResponderStateChangeHandler {
150150 ///
151151 public var handleStateChange : ( Bool ) -> Void
152152
153+ /// Allows fine-grained control over if the text field should become the first responder.
154+ ///
155+ /// This will be called when the text field's `shouldBeginEditing` delegate method is
156+ /// called and provides a final opportunity to prevent the text field becoming first responder.
157+ ///
158+ /// If the responder change was triggered programatically by a `FirstResponderDemand`
159+ /// and this returns `false` the demand will still be marked as fulfilled and reset to `nil`.
160+ public var canBecomeFirstResponder : ( ( ) -> Bool ) ?
161+
162+ /// Allows fine-grained control over if the text field should become the first responder.
163+ ///
164+ /// This will be called when the text field's `shouldEndEditing` delegate method is
165+ /// called and provides a final opportunity to prevent the text field from resigning first responder.
166+ ///
167+ /// If the responder change was triggered programatically by a `FirstResponderDemand`
168+ /// and this returns `false` the demand will still be marked as fulfilled and reset to `nil`.
169+ public var canResignFirstResponder : ( ( ) -> Bool ) ?
170+
171+ /// Initialises a state change handler with a `handleStateChange` callback.
172+ ///
173+ /// Most of the time this is the only callback that you will need to provide so this initialiser
174+ /// can be called with trailing closure syntax.
153175 public init ( handleStateChange: @escaping ( Bool ) -> Void ) {
154176 self . handleStateChange = handleStateChange
155177 }
156178
179+ public init (
180+ handleStateChange: @escaping ( Bool ) -> Void ,
181+ canBecomeFirstResponder: ( ( ) -> Bool ) ? = nil ,
182+ canResignFirstResponder: ( ( ) -> Bool ) ? = nil
183+ ) {
184+ self . handleStateChange = handleStateChange
185+ self . canBecomeFirstResponder = canBecomeFirstResponder
186+ self . canResignFirstResponder = canResignFirstResponder
187+ }
188+
157189 func callAsFunction( _ isFirstResponder: Bool ) {
158190 handleStateChange ( isFirstResponder)
159191 }
192+
193+ /// Returns a new state change handler that wraps the underlying state change handler
194+ /// in a `withAnimation` closure - this is useful if you want state changes triggered by
195+ /// a first responder state change to be explicitly animated.
196+ public func animation( ) -> Self {
197+ . init(
198+ handleStateChange: { isFirstResponder in
199+ withAnimation { self . handleStateChange ( isFirstResponder) }
200+ } ,
201+ canBecomeFirstResponder: canBecomeFirstResponder,
202+ canResignFirstResponder: canResignFirstResponder
203+ )
204+ }
160205}
161206
162207/// Represents a request to change the text field's first responder state.
@@ -238,11 +283,33 @@ extension ResponsiveTextField: UIViewRepresentable {
238283 parent. firstResponderDemandFulfilled ( )
239284 }
240285
286+ public func textFieldShouldBeginEditing( _ textField: UITextField ) -> Bool {
287+ if let canBecomeFirstResponder = parent. onFirstResponderStateChanged? . canBecomeFirstResponder {
288+ let shouldBeginEditing = canBecomeFirstResponder ( )
289+ if !shouldBeginEditing {
290+ parent. firstResponderDemandFulfilled ( )
291+ }
292+ return shouldBeginEditing
293+ }
294+ return true
295+ }
296+
241297 public func textFieldDidBeginEditing( _ textField: UITextField ) {
242298 parent. onFirstResponderStateChanged ? ( true )
243299 parent. firstResponderDemandFulfilled ( )
244300 }
245301
302+ public func textFieldShouldEndEditing( _ textField: UITextField ) -> Bool {
303+ if let canResignFirstResponder = parent. onFirstResponderStateChanged? . canResignFirstResponder {
304+ let shouldEndEditing = canResignFirstResponder ( )
305+ if !shouldEndEditing {
306+ parent. firstResponderDemandFulfilled ( )
307+ }
308+ return shouldEndEditing
309+ }
310+ return true
311+ }
312+
246313 public func textFieldDidEndEditing( _ textField: UITextField ) {
247314 parent. onFirstResponderStateChanged ? ( false )
248315 parent. firstResponderDemandFulfilled ( )
0 commit comments