@@ -47,21 +47,18 @@ project using Xcode's built-in package management tools.
47
47
### Getting Started
48
48
49
49
To 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.
53
52
54
53
``` swift
55
54
struct ExampleView : View {
56
55
@State var email: String = " "
57
- @State var isEditingEmail: Bool = false
58
56
59
57
var body: some View {
60
58
VStack {
61
59
ResponsiveTextField (
62
60
placeholder : " Email address" ,
63
- text : $email,
64
- isEditing : $isEditingEmail
61
+ text : $email
65
62
)
66
63
}
67
64
}
@@ -76,15 +73,28 @@ size using the `.fixedSize` modifier:
76
73
``` swift
77
74
ResponsiveTextField (
78
75
placeholder : " Email address" ,
79
- text : $email,
80
- isEditing : $isEditingEmail
76
+ text : $email
81
77
)
82
78
.fixedSize (horizontal : false , vertical : true )
83
79
```
84
80
85
81
As the user types in the field, it will update the state that the binding was
86
82
derived from.
87
83
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
+
88
98
### Disabling the text field
89
99
90
100
You 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:
93
103
94
104
ResponsiveTextField (
95
105
placeholder : " Email address" ,
96
- text : $email,
97
- isEditing : $isEditingEmail
106
+ text : $email
98
107
)
99
108
.disabled (true )
100
109
```
@@ -146,7 +155,6 @@ Its important to note that this configuration will be called early during the
146
155
ResponsiveTextField (
147
156
placeholder : " Email address" ,
148
157
text : $email,
149
- isEditing : $isEditingEmail,
150
158
configuration : .init {
151
159
$0 .autocorrectionType = .no
152
160
$0 .clearButtonModde = .whileEditing
@@ -179,7 +187,6 @@ You can now use this anywhere within your app in a concise way:
179
187
ResponsiveTextField (
180
188
placeholder : " Email address" ,
181
189
text : $email,
182
- isEditing : $isEditingEmail,
183
190
configuration : .emailField
184
191
)
185
192
```
@@ -224,38 +231,90 @@ public extension ResponsiveTextField.Configuration {
224
231
control over the first responder status of the control. This is one of the
225
232
major pieces of missing behaviour from the native ` TextField ` type.
226
233
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):
233
244
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
239
304
240
305
``` swift
241
306
struct ExampleView : View {
242
307
@State var email: String = " "
243
308
@State var password: String = " "
244
- @State var isEditingEmail: Bool = true
245
- @State var isEditingPassword: Bool = false
309
+ @State var emailFirstResponderDemand: FirstResponderDemand? = .shouldBecomeFirstResponder
246
310
247
311
var body: some View {
248
312
VStack {
249
313
/// This field will become first responder automatically
250
314
ResponsiveTextField (
251
315
placeholder : " Email address" ,
252
316
text : $email,
253
- isEditing : $isEditingEmail
254
- )
255
- ResponsiveTextField (
256
- placeholder : " Password" ,
257
- text : $password,
258
- isEditing : $isEditingPassword
317
+ firstResponderDemand : $emailFirstResponderDemand
259
318
)
260
319
}
261
320
}
@@ -266,16 +325,25 @@ You could also trigger the field to become first responder after a short
266
325
delay after appearing:
267
326
268
327
``` 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
+ }
275
343
}
276
344
.onAppear {
277
345
DispatchQueue.main .asyncAfter (deadline : .now () + 1 ) {
278
- isEditingEmail = true
346
+ emailFirstResponderDemand = . shouldBecomeFirstResponder
279
347
}
280
348
}
281
349
```
@@ -287,26 +355,26 @@ field to the next when the keyboard return button is tapped:
287
355
struct ExampleView : View {
288
356
@State var email: String = " "
289
357
@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 ?
292
360
293
361
var body: some View {
294
362
VStack {
295
363
/// Tapping return will make the password field first responder
296
364
ResponsiveTextField (
297
365
placeholder : " Email address" ,
298
366
text : $email,
299
- isEditing : $isEditingEmail ,
367
+ firstResponderDemand : $emailFirstResponderDemand ,
300
368
configuration : .emailField ,
301
- handleReturn : { isEditingPassword = true }
369
+ handleReturn : { passwordFirstResponderDemand = . shouldBecomeFirstResponder }
302
370
)
303
371
/// Tapping return will resign first responder and hide the keyboard
304
372
ResponsiveTextField (
305
373
placeholder : " Password" ,
306
374
text : $password,
307
- isEditing : $isEditingPassword ,
375
+ firstResponderDemand : $passwordFirstResponderDemand ,
308
376
configuration : .passwordField ,
309
- handleReturn : { isEditingPassword = false }
377
+ handleReturn : { passwordFirstResponderDemand = . shouldResignFirstResponder }
310
378
)
311
379
}
312
380
}
0 commit comments