Cécile Lebleu
Cécile Lebleu

Reputation: 196

How to stop SwiftUI from moving TextField up when the keyboard is opened?

I have a TextField near the top of the screen, followed by a spacer and more content below. The TextField moves outside of the screen when the keyboard is opened.

I've tried adding .ignoresSafeArea(.keyboard), separating the TextField from the rest of the content into two VStacks nestled in a ZStack, and a few other things found online, but nothing seems to do the trick so far.

Example:

enter image description here

Code to reproduce:

struct ContentView: View {
    @State private var text = ""
    
    var body: some View {
        VStack {
            TextField("Text", text: $text)
                .font(.largeTitle.weight(.medium))
                .padding()
            
            Spacer()
            
            VStack {
                Text("Content")
                Text("Content")
                Text("Content")
                Text("Content")
                Text("Content")
                Text("Content")
                Text("Content")
                Text("Content")
                Text("Content")
                Text("Content")
            }
            .padding()
            
            VStack {
                Text("Content")
                Text("Content")
                Text("Content")
                Text("Content")
                Text("Content")
                Text("Content")
                Text("Content")
                Text("Content")
                Text("Content")
                Text("Content")
            }
            .padding()
        }
        .ignoresSafeArea(.keyboard)
    }
}

Upvotes: 0

Views: 781

Answers (1)

MatBuompy
MatBuompy

Reputation: 2093

After some trial and error I was able to find a way to make your textfield stay on the screen (without moving) and your texts not to move at all. I had to use a GeometryReader, that for some reason keeps the TextField in place, and a ScrollView with a custom modifier that dismisses the keyboard on drag:

struct ContentView: View {
    @State private var text: String = ""

    var body: some View {
        GeometryReader { geometry in
            ScrollView(.vertical, showsIndicators: false) {
                VStack {
                    TextField("Text", text: $text)
                        .font(.largeTitle.weight(.medium))
                        .padding(.top)

                    VStack {
                        Text("Content")
                        Text("Content")
                        Text("Content")
                        Text("Content")
                        Text("Content")
                        Text("Content")
                        Text("Content")
                        Text("Content")
                        Text("Content")
                        Text("Content")
                    }
                    .padding()

                    VStack {
                        Text("Content")
                        Text("Content")
                        Text("Content")
                        Text("Content")
                        Text("Content")
                        Text("Content")
                        Text("Content")
                        Text("Content")
                        Text("Content")
                        Text("Content")
                    }
                    .padding()

                    Spacer(minLength: geometry.safeAreaInsets.bottom) // Add spacer to account for safe area bottom

                }
                .padding()
                .keyboardDismissOnDrag() // Dismiss keyboard on drag
            }
        }
    }
}

extension View {
    func keyboardDismissOnDrag() -> some View {
        modifier(KeyboardDismissOnDragGesture())
    }
}

struct KeyboardDismissOnDragGesture: ViewModifier {
    func body(content: Content) -> some View {
        content.gesture(DragGesture().onChanged { _ in
            UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
        })
    }
}

Let me know how that worked out for you!

Upvotes: 1

Related Questions