Skip to content

Commit 066a977

Browse files
author
Vitor Silveira
committed
#188: Add support to swiping to dismiss keyboard for UIKitBackend
1 parent fa6d364 commit 066a977

File tree

5 files changed

+79
-4
lines changed

5 files changed

+79
-4
lines changed

Examples/Sources/NotesExample/ContentView.swift

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@ struct ContentView: View {
3838

3939
var textEditorBackground: Color {
4040
switch colorScheme {
41-
case .light:
42-
Color(0.8, 0.8, 0.8)
43-
case .dark:
44-
Color(0.18, 0.18, 0.18)
41+
case .light:
42+
Color(0.8, 0.8, 0.8)
43+
case .dark:
44+
Color(0.18, 0.18, 0.18)
4545
}
4646
}
4747

@@ -142,6 +142,7 @@ struct ContentView: View {
142142
.padding()
143143
.background(textEditorBackground)
144144
.cornerRadius(4)
145+
.scrollDismissesKeyboard(.interactively)
145146
}
146147
}
147148
.padding()

Sources/SwiftCrossUI/Environment/EnvironmentValues.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ public struct EnvironmentValues {
7777

7878
/// Whether user interaction is enabled. Set by ``View/disabled(_:)``.
7979
public var isEnabled: Bool
80+
81+
public var scrollDismissesKeyboardMode: ScrollDismissesKeyboardMode
8082

8183
/// Called by view graph nodes when they resize due to an internal state
8284
/// change and end up changing size. Each view graph node sets its own
@@ -196,6 +198,7 @@ public struct EnvironmentValues {
196198
listStyle = .default
197199
toggleStyle = .button
198200
isEnabled = true
201+
scrollDismissesKeyboardMode = .never
199202
}
200203

201204
/// Returns a copy of the environment with the specified property set to the
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/// The ways that scrollable content can interact with the software keyboard.
2+
///
3+
/// Use this type in a call to the ``View/scrollDismissesKeyboard(_:)``
4+
/// modifier to specify the dismissal behavior of scrollable views.
5+
public enum ScrollDismissesKeyboardMode: Sendable {
6+
/// Dismiss the keyboard as soon as scrolling starts.
7+
case immediately
8+
9+
/// Enable people to interactively dismiss the keyboard as part of the
10+
/// scroll operation.
11+
///
12+
/// The software keyboard's position tracks the gesture that drives the
13+
/// scroll operation if the gesture crosses into the keyboard's area of the
14+
/// display. People can dismiss the keyboard by scrolling it off the
15+
/// display, or reverse the direction of the scroll to cancel the dismissal.
16+
case interactively
17+
18+
/// Never dismiss the keyboard automatically as a result of scrolling.
19+
case never
20+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
extension View {
2+
/// Configures the behavior in which scrollable content interacts with
3+
/// the software keyboard.
4+
///
5+
/// You use this modifier to customize how scrollable content interacts
6+
/// with the software keyboard. For example, you can specify a value of
7+
/// ``ScrollDismissesKeyboardMode/immediately`` to indicate that you
8+
/// would like scrollable content to immediately dismiss the keyboard if
9+
/// present when a scroll drag gesture begins.
10+
///
11+
/// @State private var text = ""
12+
///
13+
/// ScrollView {
14+
/// TextField("Prompt", text: $text)
15+
/// ForEach(0 ..< 50) { index in
16+
/// Text("\(index)")
17+
/// .padding()
18+
/// }
19+
/// }
20+
/// .scrollDismissesKeyboard(.immediately)
21+
///
22+
/// You can also use this modifier to customize the keyboard dismissal
23+
/// behavior for other kinds of scrollable views, like a ``List`` or a
24+
/// ``TextEditor``.
25+
///
26+
/// By default, scrollable content never automatically dismisses the keyboard.
27+
/// Pass a different value of ``ScrollDismissesKeyboardMode`` to change this behavior.
28+
/// For example, ``ScrollDismissesKeyboardMode/interactively`` allows the keyboard to dismiss
29+
/// as the user scrolls. Note that ``TextEditor`` may still use a different
30+
/// default to preserve expected editing behavior.
31+
///
32+
/// - Parameter mode: The keyboard dismissal mode that scrollable content
33+
/// uses.
34+
///
35+
/// - Returns: A view that uses the specified keyboard dismissal mode.
36+
public func scrollDismissesKeyboard(_ mode: ScrollDismissesKeyboardMode) -> some View {
37+
EnvironmentModifier(self) { environment in
38+
environment.with(\.scrollDismissesKeyboardMode, mode)
39+
}
40+
}
41+
}

Sources/UIKitBackend/UIKitBackend+Control.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,16 @@ extension UIKitBackend {
318318
textEditorWidget.child.inputAccessoryView = nil
319319
}
320320
#endif
321+
322+
textEditorWidget.child.alwaysBounceVertical = environment.scrollDismissesKeyboardMode != .never
323+
textEditorWidget.child.keyboardDismissMode = switch environment.scrollDismissesKeyboardMode {
324+
case .immediately:
325+
textEditorWidget.child.inputAccessoryView == nil ? .onDrag : .onDragWithAccessory
326+
case .interactively:
327+
textEditorWidget.child.inputAccessoryView == nil ? .interactive : .interactiveWithAccessory
328+
case .never:
329+
.none
330+
}
321331
}
322332

323333
public func setContent(ofTextEditor textEditor: Widget, to content: String) {

0 commit comments

Comments
 (0)