Maria Kravchenko
Maria Kravchenko

Reputation: 221

How to prevent keyboard from pushing up the view in SwiftUI? .ignoresSafeArea(.keyboard) and ScrollView hack doesn't work

I am a beginner coder. I develop my very first app using SwiftUI and I have a huge problem(bug) with keyboard: the View is moving up when TextField is touched. I don't want my View to move at all.

I have a ContentView(the one with the problem), that contains custom Navigation Bar and several Views.

Simplified code below.

My ContentView:

struct ContentView: View {
    var body: some View {
        
        CustomNavBarContainerPreview {
            
            ZStack {
                Color(.purple)
                    .opacity(0.4)
                    .ignoresSafeArea(.all)
                
                VStack(alignment: .center) {
                    //view with textfield
                    PullView()
                    
                    RandomView()
                    
                    LanguageView()
                    
                    Button {
                        //action
                    } label: {
                        ZStack {
                            RoundedRectangle(cornerRadius: 15)
                                .foregroundColor(.cyan)
                                .frame(minWidth: 300, idealWidth: 340, maxWidth: 340, minHeight: 70, idealHeight: 85, maxHeight: 85, alignment: .center)
                            Text("Accept Settings")
                                .font(Font.custom("Ubuntu-Regular", size: 24))
                                .foregroundColor(.white)
                        }
                        .padding(.vertical, 10)
                    }

                }
                .frame(maxWidth: .infinity, maxHeight: .infinity)
                //using this isn't prevent the view from pusing up
                .ignoresSafeArea(.keyboard, edges: .bottom)
            }
            // it doesn't work too
            .ignoresSafeArea(.keyboard)
            .onTapGesture {
                dismissKeyboard()
            }
            
        }

    }
    
    func dismissKeyboard() {
        UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.endEditing(true)
    }
}

My SubViews:

struct PullView: View {
    
    @State var num1: String = ""
    @State var num2: String = ""
    
    var body: some View {
        
        ZStack(alignment: .topLeading) {
            Rectangle()
                .frame(minWidth: 342, idealWidth: 342, maxWidth: 342, minHeight: 150, idealHeight: 178, maxHeight: 178, alignment: .center)
                .foregroundColor(.white)
            
            VStack {
                HStack {
                    Text("Title One")
                        .bold()
                        .multilineTextAlignment(.leading)
                        .foregroundColor(.black)
                        .padding(.leading, 15)
                    
                    Spacer()
                    
                    Image(systemName: "info.circle.fill")
                        .scaledToFill()
                        .frame(alignment: .trailing)
                        .padding(.trailing, 15)
                    
                }
                .frame(maxWidth: 342, maxHeight: 50)
                .padding(EdgeInsets.init(top: 0, leading: 0, bottom: 0, trailing: 0))
                .background(Rectangle()
                    .foregroundColor(Color(red: 230/255, green: 234/255, blue: 242/255))
                    .opacity(0.5))
                
                HStack(alignment: .center) {
                    
                    Text("field1")
                        .foregroundColor(.black)
                    
                    TextField("", value: $num1, formatter: NumberFormatter())
                        .textFieldStyle(DefaultTextFieldStyle())
                        .foregroundColor(.black)
                        .multilineTextAlignment(.center)
                        .frame(width: 90, height: 48)
                        .keyboardType(.numberPad)
                        .background(.green)
                    
                    Text("field2")
                        .foregroundColor(.black)
                    
                    TextField("", value: $num2, formatter: NumberFormatter())
                        .textFieldStyle(DefaultTextFieldStyle())
                        .foregroundColor(.black)
                        .multilineTextAlignment(.center)
                        .frame(width: 90, height: 48)
                        .keyboardType(.numberPad)
                        .background(.green)
                }
                .padding(.all, 10)
                
                
            }
        }
       
    }
}


struct RandomView: View {
    var body: some View {
        
        ZStack(alignment: .topLeading) {
            Rectangle()
                .frame(minWidth: 342, idealWidth: 342, maxWidth: 342, minHeight: 150, idealHeight: 178, maxHeight: 178, alignment: .center)
                .foregroundColor(.white)
            
            VStack {
                HStack {
                    Text("Title Two")
                        .bold()
                        .multilineTextAlignment(.leading)
                        .foregroundColor(.black)
                        .padding(.leading, 15)
                    
                    Spacer()
                    
                    Image(systemName: "info.circle.fill")
                        .scaledToFill()
                        .frame(alignment: .trailing)
                        .padding(.trailing, 15)
                    
                }
                .frame(maxWidth: 342, maxHeight: 50)
                .padding(EdgeInsets.init(top: 0, leading: 0, bottom: 0, trailing: 0))
                .background(Rectangle()
                    .foregroundColor(Color(red: 230/255, green: 234/255, blue: 242/255))
                    .opacity(0.5))
                
                Text("Random settings")
                Text("Subview test")
                
            }
        }
    }
}

struct LanguageView: View {
    var body: some View {
        
        ZStack(alignment: .topLeading) {
            Rectangle()
                .frame(minWidth: 342, idealWidth: 342, maxWidth: 342, minHeight: 150, idealHeight: 178, maxHeight: 178, alignment: .center)
                .foregroundColor(.white)
            
            VStack {
                HStack {
                    Text("Title Three")
                        .bold()
                        .multilineTextAlignment(.leading)
                        .foregroundColor(.black)
                        .padding(.leading, 15)
                    
                    Spacer()
                    
                    Image(systemName: "info.circle.fill")
                        .scaledToFill()
                        .frame(alignment: .trailing)
                        .padding(.trailing, 15)
                    
                }
                .frame(maxWidth: 342, maxHeight: 50)
                .padding(EdgeInsets.init(top: 0, leading: 0, bottom: 0, trailing: 0))
                .background(Rectangle()
                    .foregroundColor(Color(red: 230/255, green: 234/255, blue: 242/255))
                    .opacity(0.5))
                
                Text("Language settings")
                Text("Subview test")
                
            }
        }
    }
}

And Custom NavBarViews

struct CustomNavBar: View {
    var body: some View {
        
        ZStack {
            Rectangle()
                .foregroundColor(.clear)
                .frame(width: .infinity, height: 70)
                .background(.mint)
            
            HStack {
                Button("Back") {
                }
                Spacer()
                Text("Heading")
                    .bold()
                    .foregroundColor(.white)
                Spacer()
                Button("Next") {
                }
            }
            .padding(.horizontal)
        }
        .navigationBarHidden(true)
    }
}

struct CustomNavBarContainerPreview<Content: View>: View {
    
    let content: Content
    init(@ViewBuilder content: () -> Content) {
        self.content = content()
    }
    var body: some View {
        VStack(spacing: 0) {
            CustomNavBar()
            content
        }
    }
}

My GitHub test project here: Ready to test

What's the problem may be with this? How I can fix it?

I dug StackOverflow and didn't find the answer. I saw this is a common bug in SwiftUI. I tried to paste .ignoresSafeArea(.keyboard) and .ignoresSafeArea(.keyboard, edges: .bottom) everywhere in my code below (in ContentView, SettingsViews, custom Navigation Bar), to use Spacers() and .ignoresSafeArea(.all), and it didn't give any results: View is still moving. Also I used a hack with ScrollView to fix it, but it didn't work correct for me.

May be I can embed UIKit code to control the View and the keyboard? But I don't know how to do this.

Upvotes: 3

Views: 4519

Answers (3)

p gokul
p gokul

Reputation: 118

The issue was happening to me when the view was presented modally. What I did was the same thing: I wrapped the entire content inside a GeometryReader and applied the ignoreSafeArea modifier to that. I'm putting the code below for anyone who wants a quick reference.

GeometryReader { _ in 

     views ...

}
.ignoresSafeArea(.keyboard, edges: .all)

Upvotes: 2

Maria Kravchenko
Maria Kravchenko

Reputation: 221

Thanks to everyone who didn't pass by my problem, but unfortunately, I didn't find a suitable answer for me here. I figured it out myself, and the perfect solution for my case turned out to be placing my View in GeometryReader{} and applying the .ignoresSafeArea(.keyboard) modifier to it, and it worked!

Upvotes: 17

Torke
Torke

Reputation: 93

I know you wrote that adding a ScrollView didn't help you. I copied your code one for one into a fresh Xcode project added a ScrollView around the VStack in ContentView and for me the keyboards don't push the view up anymore after that.

Upvotes: 2

Related Questions