swiftswiftuikeyboardtextfield

Retrieving keyboard height when textfield editing starts in SwiftUI


So I have a class with a published variable called keyboardHeight that is used to retrieve the value of the keyboardHeight:

class KeyboardHeightHelper: ObservableObject {
    @Published var keyboardHeight: Double = 0
    
    init() {
        listenForKeyboardNotifications()
    }
    
    private func listenForKeyboardNotifications() {
        NotificationCenter.default.addObserver(
            forName: UIResponder.keyboardDidShowNotification,
            object: nil,
            queue: .main
        ) { notification in
            guard
                let userInfo = notification.userInfo,
                let keyboardRect = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect
            else { return }
            
            self.keyboardHeight = keyboardRect.height
        }
        
        NotificationCenter.default.addObserver(
            forName: UIResponder.keyboardDidHideNotification,
            object: nil,
            queue: .main)
        { _ in
            self.keyboardHeight = 0
        }
    }
}

Then, in ContentView I just have a TextField that should print the keyboard height when you start/stop editing the field:

import SwiftUI

struct ContentView: View {
    @State var textFieldText = ""
    @ObservedObject var keyboardHeightHelper = KeyboardHeightHelper()
    var body: some View {
    VStack {
        TextField("Text field",
                  text: $textFieldText, onEditingChanged: { _ in print("the keyboard height is \(self.keyboardHeightHelper.keyboardHeight)") })
        
    }

   }
}

The problem I have is this: When I am not editing the textfield and then click it, it prints the keyboard height is 0.0 (I guess this is because it grabs the keyboardHeight value before it presents the keyboard, so at the time the height is 0.0 as the keyboard isn't seen). When I press return and the keyboard is dismissed, the height of the keyboard (for the iPhone 8 simulator) is printed as the correct value of 260.0. My question is how do I access the value of the keyboard when I start editing?


Solution

  • The .onEditingChanged is not appropriate place to read keyboard height, because you receive this callback right in the moment of click in TextField, so there is no keyboard yet shown (and, so, no notification received).

    Instead you can listen explicitly for your keyboardHeight property publisher and be notified exactly when it is changed (what is performed on keyboard notifications synchronously, so in time)

    Here is a solution (tested with Xcode 12 / iOS 14)

    VStack {
        TextField("Text field",
                  text: $textFieldText, onEditingChanged: { _ in  })
            .onReceive(keyboardHeightHelper.$keyboardHeight) { value in
                print("the keyboard height is \(value)")
            }
    }