I want to know if the keyboard is present when the button is pressed. How would I do this? I have tried but I don't have any luck. Thanks.
4 Answers
Using this protocol, KeyboardReadable
, you can conform to any View
and get keyboard updates from it.
KeyboardReadable
protocol:
import Combine
import UIKit
/// Publisher to read keyboard changes.
protocol KeyboardReadable {
var keyboardPublisher: AnyPublisher<Bool, Never> { get }
}
extension KeyboardReadable {
var keyboardPublisher: AnyPublisher<Bool, Never> {
Publishers.Merge(
NotificationCenter.default
.publisher(for: UIResponder.keyboardWillShowNotification)
.map { _ in true },
NotificationCenter.default
.publisher(for: UIResponder.keyboardWillHideNotification)
.map { _ in false }
)
.eraseToAnyPublisher()
}
}
It works by using Combine and creating a publisher so we can receive the keyboard notifications.
With an example view of how it can be applied:
struct ContentView: View, KeyboardReadable {
@State private var text: String = ""
@State private var isKeyboardVisible = false
var body: some View {
TextField("Text", text: $text)
.onReceive(keyboardPublisher) { newIsKeyboardVisible in
print("Is keyboard visible? ", newIsKeyboardVisible)
isKeyboardVisible = newIsKeyboardVisible
}
}
}
You can now read from the isKeyboardVisible
variable to know if the keyboard is visible.
When the TextField
is active with the keyboard showing, the following prints:
Is keyboard visible? true
When the keyboard is then hidden upon hitting return, the following prints instead:
Is keyboard visible? false
You can use keyboardWillShowNotification
/keyboardWillHideNotification
to update as soon as they keyboard starts to appear or disappear, and the keyboardDidShowNotification
/keyboardDidHideNotification
variants to update after the keyboard has appeared or disappeared. I prefer the will
variant because the updates are instant for when the keyboard shows.

- 25,988
- 10
- 79
- 133
-
To be strict you should use DidShow/DidHide notifications for PO question, because Will* variants are posted *before* asked state. Sometimes it is important. – Asperi Jan 19 '21 at 04:18
-
@Asperi Good point - I have added that now, thanks! I typically prefer the `will` variant because the updates are instantly, whereas the `did` variant you have to wait for the whole appearing/disappearing animation. – George Jan 19 '21 at 09:21
-
This is a really clever answer. I try and think of ways to use Combine and I would never have thought of this. But I am clever enough to capitalise on it! – P. Ent Jul 24 '21 at 20:08
My little improvement @George's answer.
Implement publisher right inside the View protocol
import Combine
extension View {
var keyboardPublisher: AnyPublisher<Bool, Never> {
Publishers
.Merge(
NotificationCenter
.default
.publisher(for: UIResponder.keyboardWillShowNotification)
.map { _ in true },
NotificationCenter
.default
.publisher(for: UIResponder.keyboardWillHideNotification)
.map { _ in false })
.debounce(for: .seconds(0.1), scheduler: RunLoop.main)
.eraseToAnyPublisher()
}
}
I also added debounce operator in order to prevent true - false toggle when you have multiple TextFields and user moves between them.
Use in any View
struct SwiftUIView: View {
@State var isKeyboardPresented = false
@State var firstTextField = ""
@State var secondTextField = ""
var body: some View {
VStack {
TextField("First textField", text: $firstTextField)
TextField("Second textField", text: $secondTextField)
}
.onReceive(keyboardPublisher) { value in
isKeyboardPresented = value
}
}
}

- 6,056
- 5
- 41
- 53

- 201
- 2
- 6
-
Wait, what, tab indents are set to 2? You go Max, stick it to them! (I have refused to set my tabs to anything else but 2 since the eighties :-) – Yohst May 03 '23 at 18:58
iOS 15:
You can use the focused(_:)
view modifier and @FocusState
property wrapper to know whether a text field is editing, and also change the editing state.
@State private var text: String = ""
@FocusState private var isTextFieldFocused: Bool
var body: some View {
VStack {
TextField("hello", text: $text)
.focused($isTextFieldFocused)
if isTextFieldFocused {
Button("Keyboard is up!") {
isTextFieldFocused = false
}
}
}
}

- 2,698
- 3
- 16
- 32
-
19Focus and keyboard presence are not necessarily the same thing. If there is an external keyboard connected, this will not show the right result, in terms of keyboard presence. – ThomasCle Apr 06 '22 at 14:33
With Environment Key...
Taking @Lepidopteron's answer, but using it to drive an environment key.
This allows you to access the keyboard state in any view using
@Environment(\.keyboardShowing) var keyboardShowing
All you have to do is add a view modifier at the top of your hierarchy
RootView()
.addKeyboardVisibilityToEnvironment()
This is all powered by the following ViewModifier file...
public extension View {
/// Sets an environment value for keyboardShowing
/// Access this in any child view with
/// @Environment(\.keyboardShowing) var keyboardShowing
func addKeyboardVisibilityToEnvironment() -> some View {
modifier(KeyboardVisibility())
}
}
private struct KeyboardShowingEnvironmentKey: EnvironmentKey {
static let defaultValue: Bool = false
}
extension EnvironmentValues {
var keyboardShowing: Bool {
get { self[KeyboardShowingEnvironmentKey.self] }
set { self[KeyboardShowingEnvironmentKey.self] = newValue }
}
}
private struct KeyboardVisibility:ViewModifier {
#if os(macOS)
fileprivate func body(content: Content) -> some View {
content
.environment(\.keyboardShowing, false)
}
#else
@State var isKeyboardShowing:Bool = false
private var keyboardPublisher: AnyPublisher<Bool, Never> {
Publishers
.Merge(
NotificationCenter
.default
.publisher(for: UIResponder.keyboardWillShowNotification)
.map { _ in true },
NotificationCenter
.default
.publisher(for: UIResponder.keyboardWillHideNotification)
.map { _ in false })
.debounce(for: .seconds(0.1), scheduler: RunLoop.main)
.eraseToAnyPublisher()
}
fileprivate func body(content: Content) -> some View {
content
.environment(\.keyboardShowing, isKeyboardShowing)
.onReceive(keyboardPublisher) { value in
isKeyboardShowing = value
}
}
#endif
}

- 9,659
- 3
- 46
- 49
-
This is so simple...many thanks to you. For beginners (like me), make sure you add `import SwiftUI` and `import Combine` to the top of your file. I added `.addKeyboardVisibilityToEnvironment()` to the first calling of `ContentView()` in my initial `WindowGroup` and `@Environment(\.keyboardShowing) var keyboardShowing` to the top of my `ContentView` view. Everything works perfectly. Thanks again. – SPM Jun 20 '23 at 17:48