Skip to content

Commit a970a18

Browse files
committed
Added support for customising standard edit actions.
1 parent 0a15057 commit a970a18

File tree

5 files changed

+358
-36
lines changed

5 files changed

+358
-36
lines changed

CHANGELOG.md

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,37 @@
22

33
## 0.3.0
44

5+
### Added
6+
7+
* Supported standard edit actions can be customised using the `supportedStandardEditActions`
8+
parameter when initialising.
9+
* Added the ability to override and customise how standard edit actions are handled
10+
by providing a `StandardEditActionHandling` value using the `standardEditActionHandler`
11+
parameter when initialising.
12+
513
### Changed
614

7-
* Remove RunLoop tick when updating first responder state
8-
* Replace `isEditing` binding with `firstResponderState` binding.
15+
* Remove RunLoop tick when updating first responder state.
16+
* Replaced `isEditing` binding with a `firstResponderDemand` binding and an
17+
`onFirstResponderStateChanged:` callback function.
918

1019
## 0.2.0
1120

1221
### Added
1322

14-
* Delete key handling
15-
* Text change handling and prevention
16-
* Added automatic documentation generation workflow
23+
* Delete key handling.
24+
* Text change handling and prevention.
25+
* Added automatic documentation generation workflow.
1726

1827
## 0.1.0 - 2021-03-14
1928

2029
### Added
2130

22-
* Initial Release
23-
* Binding-based text handling
24-
* Binding-based first responder handling
25-
* Styling view modifiers
26-
* Secure text entry
27-
* Return key handling
28-
* Configuration system
29-
* Demo project
31+
* Initial Release.
32+
* Binding-based text handling.
33+
* Binding-based first responder handling.
34+
* Styling view modifiers.
35+
* Secure text entry.
36+
* Return key handling.
37+
* Configuration system.
38+
* Demo project.

Demo Project/ResponsiveTextFieldDemo/ContentView.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,13 @@ struct ContentView: View {
6969
emailResponderState = responderState
7070
}
7171
},
72-
handleReturn: { passwordResponderDemand = .shouldBecomeFirstResponder }
72+
handleReturn: { passwordResponderDemand = .shouldBecomeFirstResponder },
73+
supportedStandardEditActions: [],
74+
standardEditActionHandler: .init(
75+
paste: { _, _ in
76+
return false
77+
}
78+
)
7379
)
7480
.responsiveKeyboardReturnType(.next)
7581
.responsiveTextFieldTextColor(.blue)

README.md

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,22 @@ native TextField even in iOS 14.
1010

1111
## Features
1212

13-
At a high level, it provides:
14-
15-
* A simple API, making use of SwiftUI bindings to capture entered text and
16-
control the text field's first responder status.
17-
* The ability to set the text field's placeholder.
18-
* Support for secure text entry.
19-
* The ability to handle return key and delete key taps.
20-
* The ability to style the text field using SwiftUI-style view modifiers.
13+
At a high level, it provides the ability to:
14+
15+
* Use of SwiftUI bindings to capture entered text and control the text field's
16+
first responder status.
17+
* Observe and react to the text field's first responder status.
18+
* Set the text field's placeholder.
19+
* Enable secure text entry.
20+
* Easily handle return key and delete key taps with a simple callback.
21+
* Style the text field using SwiftUI-style view modifiers.
2122
* Support for enabling and disabling the text field using the SwiftUI
22-
Environment.
23-
* A composable configuration system for detailed configuration of the underlying
24-
UITextField.
25-
* Control over how and when text editing should be permitted.
23+
`.disabled` view modifier.
24+
* Configure the properties of the underlying text field using a composable
25+
text field configuration system.
26+
* Control over how and when text changes should be permitted.
27+
* Customise which standard edit actions are available (e.g. copy, paste).
28+
* Override and customise the handling of standard edit actions.
2629

2730
The following features are not currently supported:
2831

Sources/ResponsiveTextField/ResponsiveTextField.swift

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ public struct ResponsiveTextField {
3333
///
3434
/// To detect when the text field actually becomes or resigns first responder, use the
3535
/// `.onFirstResponderChange()` callback function.
36-
///
3736
var firstResponderDemand: Binding<FirstResponderDemand?>?
3837

3938
/// Allows for the text field to be configured during creation.
@@ -84,6 +83,30 @@ public struct ResponsiveTextField {
8483
/// Return `true` to allow the change or `false` to prevent the change.
8584
var shouldChange: ((String, String) -> Bool)?
8685

86+
/// A list of supported standard editing actions.
87+
///
88+
/// When set, this will override the default standard edit actions for a `UITextField`. Leave
89+
/// set to `nil` if you only want to support the default actions.
90+
///
91+
/// You can use this property and `standardEditActionHandler` to support both the
92+
/// range of standard editing actions and how each editing action should be handled.
93+
///
94+
/// If you exclude an edit action from this list, any corresponding action handler set in
95+
/// any provided `standardEditActionHandler` will not be called.
96+
var supportedStandardEditActions: Set<StandardEditAction>?
97+
98+
/// Can be set to provide custom standard editing action behaviour.
99+
///
100+
/// When `nil`, all standard editing actions will result in the default `UITextField` behaviour.
101+
///
102+
/// When set, any overridden actions will be called and if the action handler returns `true`, the
103+
/// default `UITextField` behaviour will also be called. If the action handler returns `false`,
104+
/// the default behaviour will not be called.
105+
///
106+
/// If the provided type does not implement a particular editing action, the default `UITextField`
107+
/// behaviour will be called.
108+
var standardEditActionHandler: StandardEditActionHandling<UITextField>?
109+
87110
fileprivate var shouldUpdateView: Bool = true
88111

89112
public init(
@@ -95,7 +118,9 @@ public struct ResponsiveTextField {
95118
onFirstResponderStateChanged: FirstResponderStateChangeHandler? = nil,
96119
handleReturn: (() -> Void)? = nil,
97120
handleDelete: ((String) -> Void)? = nil,
98-
shouldChange: ((String, String) -> Bool)? = nil
121+
shouldChange: ((String, String) -> Bool)? = nil,
122+
supportedStandardEditActions: Set<StandardEditAction>? = nil,
123+
standardEditActionHandler: StandardEditActionHandling<UITextField>? = nil
99124
) {
100125
self.placeholder = placeholder
101126
self.text = text
@@ -106,12 +131,8 @@ public struct ResponsiveTextField {
106131
self.handleReturn = handleReturn
107132
self.handleDelete = handleDelete
108133
self.shouldChange = shouldChange
109-
}
110-
111-
fileprivate mutating func skippingViewUpdates(_ callback: () -> Void) {
112-
shouldUpdateView = false
113-
callback()
114-
shouldUpdateView = true
134+
self.supportedStandardEditActions = supportedStandardEditActions
135+
self.standardEditActionHandler = standardEditActionHandler
115136
}
116137

117138
fileprivate mutating func firstResponderDemandFulfilled() {
@@ -153,10 +174,12 @@ public enum FirstResponderDemand: Equatable {
153174

154175
extension ResponsiveTextField: UIViewRepresentable {
155176
public func makeUIView(context: Context) -> UITextField {
156-
let textField = DeleteHandlingTextField()
177+
let textField = _UnderlyingTextField()
157178
configuration.configure(textField)
158179
// This stops the text field from expanding if the text overflows the frame width
159180
textField.handleDelete = handleDelete
181+
textField.supportedStandardEditActions = supportedStandardEditActions
182+
textField.standardEditActionHandler = standardEditActionHandler
160183
textField.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
161184
textField.placeholder = placeholder
162185
textField.text = text.wrappedValue
@@ -256,8 +279,10 @@ extension ResponsiveTextField: UIViewRepresentable {
256279
}
257280
}
258281

259-
private class DeleteHandlingTextField: UITextField {
282+
class _UnderlyingTextField: UITextField {
260283
var handleDelete: ((String) -> Void)?
284+
var supportedStandardEditActions: Set<StandardEditAction>?
285+
var standardEditActionHandler: StandardEditActionHandling<UITextField>?
261286

262287
override func deleteBackward() {
263288
handleDelete?(text ?? "")

0 commit comments

Comments
 (0)