TEAM LICENSES: Save money and learn new skills through a Hacking with Swift+ team license >>

Textfield Looses Focus on Animation

Forums > SwiftUI

In my app I am trying to achieve an animation where the navigationBarTitle disappears when we start searching for something. I've done that using the code below.

However I am noticing something super weird, as can be seen in the video, when I try to tap the textfield, the animation happens and it looses focus so the keyboard doesn't come up. This only happens the first ever time, the screen is rendered, subsequent times it works fine.

As can be seen in this video I added a delay to the onEditingChanged callback to debug and it looks like the textfield first gains focus and then the animation happens and then it looses focus. Could someone explain as to why this is happening and if there is a way to fix it?

struct SearchBar: View {
    @Binding var searchTerm: String
    @Binding var isEditing: Bool
    @Binding var hasRenderedOnce: Bool

    var executeSearch: (String) -> Void
    var resetSearch: () -> Void

    var body: some View {
        HStack {
            HStack {
                Image(systemName: "magnifyingglass")
                    .foregroundColor(Color.white)
                    .padding(.vertical)
                    .padding(.leading)
                TextField("Enter game name",
                          text: $searchTerm,
                          onEditingChanged: {
                            if $0 {
                                isEditing = true
                                hasRenderedOnce = true
                            }
                          },
                          onCommit: {
                            executeSearch(searchTerm)
                          }
                )
                if searchTerm.count > 0 {
                    Button {
                        searchTerm = ""
                    } label: {
                        Image(systemName: "multiply")
                            .foregroundColor(Color.white)
                    }.padding(.horizontal)
                    .padding(.vertical)
                }
            }
            .background(Color("BrandBackground"))
            .cornerRadius(10)

            if isEditing {
                Button {
                    searchTerm = ""
                    isEditing = false
                    resetSearch()
                    UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
                } label: {
                    Text("Cancel")
                        .font(.body)
                        .fontWeight(.bold)
                }.padding(.horizontal)
            }
        }.navigationBarHidden(isEditing)
        .if(hasRenderedOnce) {
            $0.animation(.easeIn(duration: 0.25))
        }
    }
}

2      

Solved this using

struct CustomTextField: UIViewRepresentable {

    class Coordinator: NSObject, UITextFieldDelegate {

        @Binding var text: String
        @Binding var inEditing: Bool
        @Binding var isFirstResponder: Bool
        var onSubmit: () -> Void

        init(text: Binding<String>, inEditing: Binding<Bool>, isFirstResponder: Binding<Bool>, onSubmit:@escaping () -> Void) {
            _text = text
            self.onSubmit = onSubmit
            _inEditing = inEditing
            _isFirstResponder = isFirstResponder
        }

        func textFieldDidChangeSelection(_ textField: UITextField) {
            text = textField.text ?? ""
        }

        func textFieldShouldReturn(_ textField: UITextField) -> Bool {
            isFirstResponder = false
            self.onSubmit()
            return true
        }
    }

    @Binding var text: String
    @Binding var inEditing: Bool
    @Binding var isFirstResponder: Bool
    var onSubmit: () -> Void

    func makeUIView(context: UIViewRepresentableContext<CustomTextField>) -> UITextField {
        let textField = UITextField(frame: .zero)
        textField.delegate = context.coordinator
        return textField
    }

    func makeCoordinator() -> CustomTextField.Coordinator {
        return Coordinator(text: $text, inEditing: $inEditing, isFirstResponder: $isFirstResponder, onSubmit: onSubmit)
    }

    func updateUIView(_ uiView: UITextField, context: UIViewRepresentableContext<CustomTextField>) {
        uiView.text = text
        if isFirstResponder {
            uiView.becomeFirstResponder()
        } else {
            uiView.resignFirstResponder()
        }
    }
}

with the usage being something like

struct SearchBar: View {
    @Binding var searchTerm: String
    @Binding var isEditing: Bool
    @Binding var hasRenderedOnce: Bool
    @State var showKeyboard: Bool = false

    var executeSearch: (String) -> Void
    var resetSearch: () -> Void

    var body: some View {
        HStack {
            HStack {
                Image(systemName: "magnifyingglass")
                    .foregroundColor(Color.white)
                    .padding(.vertical)
                    .padding(.leading)
                CustomTextField(text: $searchTerm,
                                inEditing: $isEditing,
                                isFirstResponder: $showKeyboard) {
                    showKeyboard = false
                    executeSearch(searchTerm)
                }.frame(height: 50)
                .onTapGesture {
                    showKeyboard = true
                    isEditing = true
                    hasRenderedOnce = true
                }
                if searchTerm.count > 0 {
                    Button {
                        searchTerm = ""
                    } label: {
                        Image(systemName: "multiply")
                            .foregroundColor(Color.white)
                    }.padding(.horizontal)
                    .padding(.vertical)
                }
            }
            .background(Color("BrandBackground"))
            .cornerRadius(10)

            if isEditing {
                Button {
                    searchTerm = ""
                    isEditing = false
                    showKeyboard = false
                    resetSearch()
                } label: {
                    Text("Cancel")
                        .font(.body)
                        .fontWeight(.bold)
                }.padding(.horizontal)
            }
        }.navigationBarHidden(isEditing)
        .if(hasRenderedOnce) {
            $0.animation(.easeIn(duration: 0.25))
        }
    }
}

2      

Hacking with Swift is sponsored by RevenueCat.

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.