Created
March 5, 2022 08:22
-
-
Save TheWorstProgrammerEver/d78dc10ec74eb0d75eea6640cf56ccf0 to your computer and use it in GitHub Desktop.
SwiftUI keyboard dismissal / focus lost handler.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
struct Example : View { | |
@State private var first: String = "" | |
@State private var second: String = "" | |
var body: some View { | |
Form { | |
TextField("First", text: $first) | |
.onFocusLost { /* save value, etc */ } | |
TextEditor(text: $second) | |
.onFocusLost { /* save value, etc */ } | |
} | |
.keyboardDismissalEnabled() | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
// Sometimes you want to do something whenever the user has finished inputting text into a text field / text editor. | |
// A TextField has the onCommit modifier which is triggered when the user presses the "Return" key. | |
// A TextEditor also has this modifier but it is not triggered by pressing the "Return" key. | |
// Most users simply expect that upon dismissing the keyboard the value will be saved. | |
// However, we can't rely on onCommit because: | |
// 1. It is not consistent between fields, and | |
// 2. It is not invoked if the user does not explicitly press the "Return" key. | |
// It may also not be performant to save on every value update. | |
// In lieu of a debounce or throttle (with Combine) a more UX-centric approach is required. | |
// This approach lets you respond to the event where a user | |
// "has finished inputting text and (via whatever means) dismisses the keyboard". | |
extension View { | |
// Enables a Keyboard toolbar button to dismiss the keyboard. Put this on a root element (like a Form). | |
// Ensure to only add one per hierarchy. | |
func keyboardDismissalEnabled() -> some View { | |
modifier(HandlesKeyboardDismissalModifier()) | |
} | |
// Registers an action callback to be invoked when focus is lost on the text field | |
// (whether by keyboard dismissal or otherwise). | |
func onFocusLost(_ action: @escaping () -> Void) -> some View { | |
modifier(OnFocusLostModifier(action: action)) | |
} | |
} | |
struct OnFocusLostModifier : ViewModifier { | |
var action: () -> Void | |
@FocusState private var focus: Bool | |
func body(content: Content) -> some View { | |
content | |
.focused($focus) | |
.onChange(of: focus) { v in | |
if !v { action() } | |
} | |
} | |
} | |
struct HandlesKeyboardDismissalModifier : ViewModifier { | |
func body(content: Content) -> some View { | |
content | |
.toolbar { | |
ToolbarItemGroup(placement: .keyboard) { | |
HStack { | |
Spacer() | |
Button(action: hideKeyboard) { | |
Text("Done") | |
} | |
} | |
} | |
} | |
} | |
} | |
fileprivate func hideKeyboard() { | |
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment