Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

swiftui - how to avoid 'Unable to simultaneously satisfy constraints' error?

I am trying to apply the code in the accepted answer to this question, on how to make a SwiftUI TextField become a first responder. Here is the code, copied from that answer, which I tried to use in xcode:

struct CustomTextField: UIViewRepresentable {

    class Coordinator: NSObject, UITextFieldDelegate {

        @Binding var text: String
        var didBecomeFirstResponder = false

        init(text: Binding<String>) {
            _text = text
        }

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

    }

    @Binding var text: String
    var isFirstResponder: Bool = false

    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)
    }

    func updateUIView(_ uiView: UITextField, context: UIViewRepresentableContext<CustomTextField>) {
        uiView.text = text
        if isFirstResponder && !context.coordinator.didBecomeFirstResponder  {
            uiView.becomeFirstResponder()
            context.coordinator.didBecomeFirstResponder = true
        }
    }
}

Running that code, however, gives me a run-time error, saying 'Unable to simultaneously satisfy constraints.' The error is shown below.

in makeUIView

in updateUIView in onEditingChanged 2020-08-14 16:02:48.445045-0600 OpenRussian[2965:122005] [LayoutConstraints] Unable to simultaneously satisfy constraints. Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. ( "<NSLayoutConstraint:0x600002580d20 'assistantHeight' TUISystemInputAssistantView:0x7fde585064a0.height == 44 (active)>", "<NSLayoutConstraint:0x600002591b30 'assistantView.bottom' TUISystemInputAssistantView:0x7fde585064a0.bottom == _UIKBCompatInputView:0x7fde51b78c50.top (active)>", "<NSLayoutConstraint:0x600002591ae0 'assistantView.top' V:|-(0)-[TUISystemInputAssistantView:0x7fde585064a0] (active, names: '|':UIInputSetHostView:0x7fde58518070 )>", "<NSLayoutConstraint:0x600002591720 'inputView.top' V:|-(0)-[_UIKBCompatInputView:0x7fde51b78c50] (active, names: '|':UIInputSetHostView:0x7fde58518070 )>" )

Will attempt to recover by breaking constraint <NSLayoutConstraint:0x600002591b30 'assistantView.bottom' TUISystemInputAssistantView:0x7fde585064a0.bottom == _UIKBCompatInputView:0x7fde51b78c50.top (active)>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger. The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.

What can I modify to make this work, and avoid this 'unable to simultaneously satisfy constraints' run-time error?

like image 518
mherzl Avatar asked Aug 14 '20 21:08

mherzl


Video Answer


2 Answers

Let's look at these one by one:

<NSLayoutConstraint:0x600002580d20 'assistantHeight' TUISystemInputAssistantView:0x7fde585064a0.height == 44 (active)>

This is saying view 0x7fde585064a0 is active and has 44 points height

<NSLayoutConstraint:0x600002591ae0 'assistantView.top' V:|-(0)-[TUISystemInputAssistantView:0x7fde585064a0] (active, names: '|':UIInputSetHostView:0x7fde58518070 )>

This is saying that 0x7fde585064a0's top edge must be a gap of 0 points from the top of its superview 0x7fde58518070

<NSLayoutConstraint:0x600002591b30 'assistantView.bottom' TUISystemInputAssistantView:0x7fde585064a0.bottom == _UIKBCompatInputView:0x7fde51b78c50.top (active)>

This is saying view 0x7fde51b78c50 and 0x7fde585064a0 are conflicting. These four things can't all be true. And the problem with constraints 0x600002591b30. Find out where the problem is and set true constraint.

I think it's useful to know the basics and understand what Apple / Xcode is trying to tell you via logs:

H = Horizontal constraint(for leading and Trailing)
V = Vertical constraint(top and bottom edge)
h = height
w = width

TopEdge    -> V:|-(points)-[VIEW:memoryAddress] 
BottomEdge -> V:[VIEW:memoryAddress]-(points)-|
Leading    -> H:|-(points)-[VIEW:memoryAddress] 
Trailing   -> H:[VIEW:memoryAddress] -(points)-|
height     -> h= --& v=--& V:[VIEW:memoryAddress((points)] 
width      -> VIEW:memoryAddress.width == points 
between    -> H:[VIEW 1]-(51)-[VIEW 2]
like image 106
Eldar Avatar answered Nov 15 '22 05:11

Eldar


Try a bit corrected variant provided below (with testing view). Tested with Xcode 12 / iOS 14 - no console warnings observed.

struct CustomTextField: UIViewRepresentable {

    class Coordinator: NSObject, UITextFieldDelegate {

        @Binding var text: String
        var didBecomeFirstResponder = false

        init(text: Binding<String>) {
            _text = text
        }

        func textFieldDidChangeSelection(_ textField: UITextField) {
            DispatchQueue.main.async {
                self.text = textField.text ?? ""
            }
        }

    }

    @Binding var text: String
    var isFirstResponder: Bool = false

    func makeUIView(context: UIViewRepresentableContext<CustomTextField>) -> UITextField {
        let textField = UITextField(frame: .zero)
        textField.delegate = context.coordinator
        textField.setContentHuggingPriority(.defaultHigh, for: .vertical)
        textField.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)

        DispatchQueue.main.async {
            textField.becomeFirstResponder()
        }
        return textField
    }

    func makeCoordinator() -> CustomTextField.Coordinator {
        return Coordinator(text: $text)
    }

    func updateUIView(_ uiView: UITextField, context: UIViewRepresentableContext<CustomTextField>) {
        uiView.text = text
    }
}

struct TestConstraintsError: View {
    @State private var text = ""
    var body: some View {
        VStack {
            Text("Input: " + text)
            CustomTextField(text: $text, isFirstResponder: true)
                .padding()
                .border(Color.red)
        }
    }
}
like image 22
Asperi Avatar answered Nov 15 '22 05:11

Asperi