@@ -47,21 +47,18 @@ project using Xcode's built-in package management tools.
4747### Getting Started
4848
4949To use ` ResponsiveTextField ` you will need to provide it with, at a minimum,
50- a placeholder string, a ` Binding<String> ` to capture the text entered into the
51- text field and a ` Binding<Bool> ` to manage the text field's first responder
52- status.
50+ a placeholder string and a ` Binding<String> ` to capture the text entered into
51+ the text field.
5352
5453``` swift
5554struct ExampleView : View {
5655 @State var email: String = " "
57- @State var isEditingEmail: Bool = false
5856
5957 var body: some View {
6058 VStack {
6159 ResponsiveTextField (
6260 placeholder : " Email address" ,
63- text : $email,
64- isEditing : $isEditingEmail
61+ text : $email
6562 )
6663 }
6764 }
@@ -76,15 +73,28 @@ size using the `.fixedSize` modifier:
7673``` swift
7774ResponsiveTextField (
7875 placeholder : " Email address" ,
79- text : $email,
80- isEditing : $isEditingEmail
76+ text : $email
8177)
8278.fixedSize (horizontal : false , vertical : true )
8379```
8480
8581As the user types in the field, it will update the state that the binding was
8682derived from.
8783
84+ You can enable secure text entry by passing in the ` isSecure ` property:
85+
86+ ``` swift
87+ ResponsiveTextField (
88+ placeholder : " Email address" ,
89+ text : $email,
90+ isSecure : true
91+ )
92+ ```
93+
94+ The ` isSecure ` property can be updated when the view is updated so it is
95+ possible to control this via some external state property, i.e. to dynamically
96+ enable or disable secure text entry.
97+
8898### Disabling the text field
8999
90100You can disable the text field using the standard SwiftUI ` .disabled ` modifier:
@@ -93,8 +103,7 @@ You can disable the text field using the standard SwiftUI `.disabled` modifier:
93103
94104ResponsiveTextField (
95105 placeholder : " Email address" ,
96- text : $email,
97- isEditing : $isEditingEmail
106+ text : $email
98107)
99108.disabled (true )
100109```
@@ -146,7 +155,6 @@ Its important to note that this configuration will be called early during the
146155ResponsiveTextField (
147156 placeholder : " Email address" ,
148157 text : $email,
149- isEditing : $isEditingEmail,
150158 configuration : .init {
151159 $0 .autocorrectionType = .no
152160 $0 .clearButtonModde = .whileEditing
@@ -179,7 +187,6 @@ You can now use this anywhere within your app in a concise way:
179187ResponsiveTextField (
180188 placeholder : " Email address" ,
181189 text : $email,
182- isEditing : $isEditingEmail,
183190 configuration : .emailField
184191)
185192```
@@ -224,38 +231,90 @@ public extension ResponsiveTextField.Configuration {
224231control over the first responder status of the control. This is one of the
225232major pieces of missing behaviour from the native ` TextField ` type.
226233
227- The control is passed a ` Binding<Bool> ` on initialisation which allows two-way
228- communication about the text field's responder state. When the user taps on
229- the text field, it will become first responder unless it has been disabled.
230- This will update the state that the binding was derived from to ` true ` .
231- Similarly, if another control becomes first responder, the text field will
232- resign it's first responder status and set the underlying state to ` false ` .
234+ ### Observing the first responder state
235+
236+ When initialised you can pass in a callback function using the parameter
237+ ` onFirstResponderStateChanged: ` - this takes a closure that will be called
238+ with the updated ` FirstResponderState ` whenever it changes, either as a result
239+ of some user interaction or as the result of a change in the
240+ ` FirstResponderDemand ` (see below).
241+
242+ If you need to track this state you can store it in some external state, such as
243+ an ` @State ` property or an ` @ObservableObject ` (like your view model):
233244
234- Update the external state will update the text field and will make it become
235- or resign first responder. For example, on a screen with two text fields, you
236- could make the first text field become first responder automatically, causing
237- the keyboard to appear when the view is shown, by simply setting the default
238- value of the state to ` true ` :
245+ ``` swift
246+ struct ExampleView : View {
247+ @State
248+ var responderState: FirstResponderState = .notFirstResponder
249+
250+ var body: some View {
251+ ResponsiveTextField (
252+ placeholder : " Email address" ,
253+ text : $email,
254+ configuration : .emailField ,
255+ onFirstResponderStateChanged : { responderState = $0 }
256+ )
257+ }
258+ }
259+ ```
260+
261+ ### Progamatically controlling the first responder state
262+
263+ ` ResponsiveTextField ` also supports binding-based control over the field's
264+ first responder state. To control the first responder state, you must
265+ initialise the field with a ` Binding<FirstResponderDemand?> ` :
266+
267+ ``` swift
268+ struct ExampleView : View {
269+ @State
270+ var responderDemand: FirstResponderDemand?
271+
272+ var body: some View {
273+ ResponsiveTextField (
274+ placeholder : " Email address" ,
275+ text : $email,
276+ firstResponderDemand : $responderDemand
277+ )
278+ }
279+ }
280+ ```
281+
282+ Whenever the binding's wrapped value changes, it will attempt to trigger a
283+ responder state change unless the text field's current responder state already
284+ fulfils the demand. Once the demand has been fulfilled the binding's wrapped
285+ value will be set back to ` nil ` .
286+
287+ #### Becoming first responder
288+
289+ To make the text field become first responder, set the demand to
290+ ` .shouldBecomeFirstResponder ` . If the text field is already first responder the
291+ binding's wrapped value will be automatically set back to ` nil ` , otherwise
292+ ` becomeFirstResponder() ` will be called and the binding's wrapped value will
293+ be set to ` nil ` once the first responder state has become ` isFirstResponder ` .
294+
295+ #### Resigning first responder
296+
297+ To make the text field resign first responder, set the demand to
298+ ` .shouldResignFirstResponder ` . If the text field is not the first responder the
299+ binding's wrapped value will be automatically set back to ` nil ` , otherwise
300+ ` resignFirstResponder() ` will be called and the binding's wrapped value will
301+ be set to ` nil ` once the first responder state has become ` notFirstResponder ` .
302+
303+ ### Example: Using ` @State ` to become first responder on view appear
239304
240305``` swift
241306struct ExampleView : View {
242307 @State var email: String = " "
243308 @State var password: String = " "
244- @State var isEditingEmail: Bool = true
245- @State var isEditingPassword: Bool = false
309+ @State var emailFirstResponderDemand: FirstResponderDemand? = .shouldBecomeFirstResponder
246310
247311 var body: some View {
248312 VStack {
249313 /// This field will become first responder automatically
250314 ResponsiveTextField (
251315 placeholder : " Email address" ,
252316 text : $email,
253- isEditing : $isEditingEmail
254- )
255- ResponsiveTextField (
256- placeholder : " Password" ,
257- text : $password,
258- isEditing : $isEditingPassword
317+ firstResponderDemand : $emailFirstResponderDemand
259318 )
260319 }
261320 }
@@ -266,16 +325,25 @@ You could also trigger the field to become first responder after a short
266325delay after appearing:
267326
268327``` swift
269- VStack {
270- ResponsiveTextField (
271- placeholder : " Email address" ,
272- text : $email,
273- isEditing : $isEditingEmail
274- )
328+ struct ExampleView : View {
329+ @State var email: String = " "
330+ @State var password: String = " "
331+ @State var emailFirstResponderDemand: FirstResponderDemand?
332+
333+ var body: some View {
334+ VStack {
335+ /// This field will become first responder automatically
336+ ResponsiveTextField (
337+ placeholder : " Email address" ,
338+ text : $email,
339+ firstResponderDemand : $emailFirstResponderDemand
340+ )
341+ }
342+ }
275343}
276344.onAppear {
277345 DispatchQueue.main .asyncAfter (deadline : .now () + 1 ) {
278- isEditingEmail = true
346+ emailFirstResponderDemand = . shouldBecomeFirstResponder
279347 }
280348}
281349```
@@ -287,26 +355,26 @@ field to the next when the keyboard return button is tapped:
287355struct ExampleView : View {
288356 @State var email: String = " "
289357 @State var password: String = " "
290- @State var isEditingEmail: Bool = true
291- @State var isEditingPassword: Bool = false
358+ @State var emailFirstResponderDemand: FirstResponderDemand ? = . shouldBecomeFirstResponder
359+ @State var passwordFirstResponderDemand: FirstResponderDemand ?
292360
293361 var body: some View {
294362 VStack {
295363 /// Tapping return will make the password field first responder
296364 ResponsiveTextField (
297365 placeholder : " Email address" ,
298366 text : $email,
299- isEditing : $isEditingEmail ,
367+ firstResponderDemand : $emailFirstResponderDemand ,
300368 configuration : .emailField ,
301- handleReturn : { isEditingPassword = true }
369+ handleReturn : { passwordFirstResponderDemand = . shouldBecomeFirstResponder }
302370 )
303371 /// Tapping return will resign first responder and hide the keyboard
304372 ResponsiveTextField (
305373 placeholder : " Password" ,
306374 text : $password,
307- isEditing : $isEditingPassword ,
375+ firstResponderDemand : $passwordFirstResponderDemand ,
308376 configuration : .passwordField ,
309- handleReturn : { isEditingPassword = false }
377+ handleReturn : { passwordFirstResponderDemand = . shouldResignFirstResponder }
310378 )
311379 }
312380 }
0 commit comments