4

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.

enter image description hereenter image description here

Is there a simple solution in SwiftUI to keep the textfield always on keyboard's top ?

3 Answers 3

5

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

Sign up to request clarification or add additional context in comments.

1 Comment

that's crashing Fatal error: Accessing State<CGFloat> outside View.body: file
4

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)
  }
}

Comments

-1

Just gonna drop a simple, easy and a bit hacky solution I found to handle the keyboard:

struct ContentView : View {

 @State private var name = ""
 @State private var keyboardUp = false
 private var keyboardHeight: CGFloat { 150 }

    var body: some View {
        NavigationView {
            Spacer()
            Form {
                TextField($name, placeholder: Text("Name"), onEditingChanged: { { self.keyboardUp = $0 })
            }.offset(y: keyboardUp ? -keyboardHeight : 0)
            .animation(.basic())
            .navigationBarTitle(Text("Title"))
        }
    }
}

1 Comment

This solution doesn't work for all devices, as keyboard may have different sizes on different devices.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.