0

I am using a boolean (searchVM.showSearchView: Bool) to simultaneously turn opacity of a SearchView to 1.0 AND show/hide a UIRepresentableKeyboard.

This gives me the same functionality as Google Maps when you tap on the search bar and (simultaneously) the white search view appears, textfield focused, and keyboard showing.

However I am getting the below message print twice every time I set the showSearchView to true.

=== AttributeGraph: cycle detected through attribute 320536 ===

=== AttributeGraph: cycle detected through attribute 318224 ===

Why am I getting this message? Is it the passing around of my searchVM?

SearchViewModel

class SearchViewModel: ObservableObject {

    var text: String = ""    

    @Published var showSearchView: Bool = false 
 
}

Textfield & Cancel button

struct SearchBar: View {
    @ObservedObject var searchVM: SearchViewModel
    @State var text: String = ""
   
    var body: some View {
        HStack {
            FirstResponderTextfield(searchVM: searchVM, text: $text, placeholder: "Search")
            Text("Cancel")
                .onTapGesture {
                    searchVM.showSearchView.toggle()
                }
        }
    }
}

FirstResponderTextField

struct FirstResponderTextfield: UIViewRepresentable {
    
    @ObservedObject var searchVM : SearchViewModel

    @Binding var text: String
    let placeholder: String

    // COORDINATOR
    class TextFieldCoordinator: NSObject, UITextFieldDelegate {

        var control: FirstResponderTextfield
        
        @Binding var text: String // unused but required

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

        func textFieldDidEndEditing(_ textField: UITextField) {
            
        }

        init(control: FirstResponderTextfield, text: Binding<String>) {
            self.control = control
            self._text = text
        }
    }

    func makeCoordinator() -> TextFieldCoordinator {
        return TextFieldCoordinator(control: self, text: $text)
    }

    func makeUIView(context: Context) -> some UIView {
        let textField = UITextField()
        textField.delegate = context.coordinator
        textField.placeholder = placeholder
        return textField
    }

    func updateUIView(_ uiView: UIViewType, context: Context) {
        if searchVM.showSearchView {
            uiView.becomeFirstResponder() // show keyboard
        } else {
            uiView.resignFirstResponder() // dismiss keyboard
        }
    }
}

Jake Smith
  • 580
  • 3
  • 11
  • 1
    This might be of some use to you https://stackoverflow.com/questions/62869586/how-do-i-debug-swiftui-attributegraph-cycle-warnings – Andrew Dec 05 '21 at 21:36

1 Answers1

1

I fixed this by adding DispatchQueue.asyncAfter( .now() + 0.05). Probably a mickey-mouse way of doing things but it works!

 func updateUIView(_ uiView: UIViewType, context: Context) {
        // here we check to see if our textfield has become first responder
        if searchVM.showSearchView {
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) {
                uiView.becomeFirstResponder() // show keyboard
            }
           
        } else {
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) {
                uiView.resignFirstResponder() // dismiss keyboard
            }
        }
    }
Jake Smith
  • 580
  • 3
  • 11