i have this
struct ContentView: View {
    var body: some View {
        ZStack(alignment: Alignment.bottom) {
            List {
                Text("Default text").foregroundColor(Color.red)
            }
            TextField("Placeholder", text: .constant(""))
                .frame(minHeight: 30)
                .cornerRadius(8.0)
                .padding(10)
                .background(Color.blue)
        }
    }
}
Actually, when i focus on the TextField, keyboard hide this textfield.


Is there a simple solution in SwiftUI to keep the textfield always on keyboard's top ?
Here is a snippet that observes NotificationCenter notifications related the keyboard, and changes the height of a spacer view based on the computed keyboard height.
import Combine
struct ExampleView: View {
  @State var keyboardHeight: CGFloat = 0
  var cancellables: Set<AnyCancellable> = []
  init() {
      NotificationCenter.default.publisher(for: UIResponder.keyboardWillChangeFrameNotification)
        .merge(with: NotificationCenter.default.publisher(for: UIResponder.keyboardWillHideNotification))
        .compactMap({ notification in
          guard let keyboardFrameValue: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else { return nil }
          let keyboardFrame = keyboardFrameValue.cgRectValue
          // If the rectangle is at the bottom of the screen, set the height to 0.
          if keyboardFrame.origin.y == UIScreen.main.bounds.height {
            return 0
          } else {
            // Adjust for safe area
            return keyboardFrame.height - (UIApplication.shared.windows.first?.safeAreaInsets.bottom ?? 0)
          }
        })
        .assign(to: \.keyboardHeight, on: self)
        .store(in: &cancellables)
  }
  var body: some View {
    VStack {
      // Your content here
      Spacer()
        .frame(height: keyboardHeight)
    }
  }
}
I wrote a Swift package that handles this for you. It exposes a KeyboardObservingView that wraps your content.
It's available here: https://github.com/nickffox/KeyboardObserving
You would use it like this:
var body: some View {
  KeyboardObservingView {
    List {...}
    TextField("Placeholder", text: .constant(""))
      .frame(minHeight: 30)
      .cornerRadius(8.0)
      .padding(10)
      .background(Color.blue)
  }
}
Here's an image of the package in use: demo
I've tried different approaches, and none of them worked for me. This one below is the only one that worked for different devices.
Add this extension in a file:
    import SwiftUI
    import Combine
    extension View {
      func keyboardSensible(_ offsetValue: Binding<CGFloat>) -> some View {
        return self
            .padding(.bottom, offsetValue.wrappedValue)
            .animation(.spring())
            .onAppear {
            NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillShowNotification, object: nil, queue: .main) { notification in
                let keyWindow = UIApplication.shared.connectedScenes
                    .filter({$0.activationState == .foregroundActive})
                    .map({$0 as? UIWindowScene})
                    .compactMap({$0})
                    .first?.windows
                    .filter({$0.isKeyWindow}).first
                let bottom = keyWindow?.safeAreaInsets.bottom ?? 0
                let value = notification.userInfo![UIResponder.keyboardFrameEndUserInfoKey] as! CGRect
                let height = value.height
                offsetValue.wrappedValue = height - bottom
            }
            NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillHideNotification, object: nil, queue: .main) { _ in
                offsetValue.wrappedValue = 0
            }
        }
      }
}
In your view, you need a variable to bind offsetValue:
struct IncomeView: View {
  @State private var offsetValue: CGFloat = 0.0
  var body: some View { 
    VStack {
     //...       
    }
    .keyboardSensible($offsetValue)
  }
}
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With