skip to Main Content

I implemented a y-offset mechanism for my TextField when the keyboard is visible. On tap, the entire screen is moved up. Now, I want to tap on any empty space to dismiss the keyboard. The problem is that the added space granted by the offset does not receive my tap gesture.

struct LoginView: View {
    @State private var email = ""
    @State private var password = ""

    @State private var keyboardYOffset = CGFloat(0)

    var body: some View {
        VStack {
            VStack(spacing: 32) {

             Rectangle()
                .frame(width: 187, height: 208)
                .padding(.top, 56)
                .padding(.bottom, 94)
                .background(Color.red)

                TextField("Email", text: $email)
                    .frame(maxWidth: .infinity)
                    .frame(height: 50)
                // inset of the placeholder text
                    .padding(.horizontal)
                    .border(Color.red)
                // inset of the entire textfield
                    .padding(.horizontal)


                SecureField("Password", text: $email)
                    .frame(maxWidth: .infinity)
                    .frame(height: 50)
                // inset of the placeholder text
                    .padding(.horizontal)
                    .border(Color.red)
                // inset of the entire textfield
                    .padding(.horizontal)


                Spacer()
            }
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .padding(.top, 32)
            .background(Color.white.ignoresSafeArea())
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
        .background(Theme.BrandColor.primary.ignoresSafeArea())
        .offset(y: -keyboardYOffset)
        .animation(.easeInOut(duration: 1), value: keyboardYOffset)
        .onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillShowNotification)) { notification in
            let value = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect
            let keyboardHeight = value?.height ?? 0
            // for our purposes we only need to offset by half the keyboard height
            self.keyboardYOffset = keyboardHeight / 2
        }
        .onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillHideNotification)) { _ in
            self.keyboardYOffset = 0
        }
        .onTapGesture {
            UIApplication.shared.endEditing()
        }
    }
}

struct LoginView_Previews: PreviewProvider {
    static var previews: some View {
        LoginView()
    }
}

extension UIApplication {
    func endEditing() {
        sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
    }
}

I’ve tried adding a tap gesture recogniser everywhere to no avail.

2

Answers


  1. Chosen as BEST ANSWER

    fixed it by adding a contentShape modifier to the outermost VStack

    .contentShape(Rectangle())
    .onTapGesture {
       UIApplication.shared.endEditing()
    }
    

  2. Try this:

        import SwiftUI
    
    struct LoginView: View {
        @State private var email = ""
        @State private var password = ""
        
        @State private var keyboardYOffset = CGFloat(0)
        
        var body: some View {
            VStack {
                VStack(spacing: 32) {
                    Rectangle()
                        .frame(width: 187, height: 208)
                        .padding(.top, 56)
                        .padding(.bottom, 94)
                        .background(Color.red)
                    
                    TextField("Email", text: $email)
                        .frame(maxWidth: .infinity)
                        .frame(height: 50)
                        .padding(.horizontal)
                        .border(Color.red)
                        .padding(.horizontal)
                    
                    SecureField("Password", text: $password)
                        .frame(maxWidth: .infinity)
                        .frame(height: 50)
                        .padding(.horizontal)
                        .border(Color.red)
                        .padding(.horizontal)
                    
                    Spacer()
                }
                .frame(maxWidth: .infinity, maxHeight: .infinity)
                .padding(.top, 32)
                .background(Color.white.ignoresSafeArea().onTapGesture {
                    UIApplication.shared.endEditing()
                })
            }
            .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
            .background(Color.blue.ignoresSafeArea())
            .offset(y: -keyboardYOffset)
            .animation(.easeInOut(duration: 1), value: keyboardYOffset)
            .onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillShowNotification)) { notification in
                let value = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect
                let keyboardHeight = value?.height ?? 0
                self.keyboardYOffset = keyboardHeight / 2
            }
            .onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillHideNotification)) { _ in
                self.keyboardYOffset = 0
            }
        }
    }
    
    struct LoginView_Previews: PreviewProvider {
        static var previews: some View {
            LoginView()
        }
    }
    
    extension UIApplication {
        func endEditing() {
            windows.forEach { $0.endEditing(false) }
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search