How to disable TextField with Toogle in SwiftUI?
Asked Answered
C

3

6

How should TextField be properly disabled when using Toogle in swiftUI? The following simple example causes warnings in the console if we disable a TextField that has some value entered in it, deleting it doesn't fix the situation either.

struct ContentView: View {
@State var isToogleOn = false
@State var textFieldValue = ""

var body: some View {
    HStack {
        TextField("Placeholder", text: $textFieldValue)
            .disabled(!isToogleOn)
        Toggle("Activate textField", isOn: $isToogleOn)
        }
    }
}

Warnings when textfield is switched off:

=== AttributeGraph: cycle detected through attribute 160396 === 2022-01-08 15:27:46.182588+0100 CrochetIo[15460:1558507] [SwiftUI] Modifying state during view update, this will cause undefined behavior

Computer answered 10/1, 2022 at 12:0 Comment(0)
C
5

The reason given by @Asperi is correct, but I would suggest other solution. When switching Toogle off, let's drop the focus on the TextField and only then disable it.

    @State var isToogleOn = false
    @State var textFieldIsDisabled = false
    @State var textFieldValue = ""
    
    @FocusState private var focusField: Field?
    
    var body: some View {
        HStack {
            TextField("Placeholde", text: $textFieldValue)
                .focused($focusField, equals: .textField1)
                .disabled(textFieldIsDisabled)
            Toggle("Activate textField", isOn: $isToogleOn)
                .onChange(of: isToogleOn) { newValue in
                    focusField = nil
                    textFieldIsDisabled = !newValue
                }
        }
    }
}
Computer answered 10/1, 2022 at 14:15 Comment(0)
R
1

Actually it is Apple's logs, so we should not care about them, but if they disturb you, then it is possible to solve that, because they are posted due to TextField is still in focus during disable.

The possible approach is to use proxy binding on toggle with injected pre-set side-effect.

Tested with Xcode 13.2 / iOS 15.2

struct ContentViewTestToggleText: View {
    @State var isToogleOn = false
    @State var textFieldValue = ""

    var body: some View {
        HStack {
            TextField("Placeholder", text: $textFieldValue)
                .disabled(!isToogleOn)

            let onToggle = Binding(
                get: { self.isToogleOn },
                set: {
                    if !$0 {
                        UIApplication.shared.endEditing()     // << here !!
                    }
                    
                    self.isToogleOn = $0
                }
            )
            Toggle("Activate textField", isOn: onToggle)
        }
    }
}

extension UIApplication {
    func endEditing() {
        sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
    }
}
Ravishing answered 10/1, 2022 at 12:19 Comment(0)
P
0

I have found that a slightly more straightforward approach is:

struct ContentView: View {
    @State private var isToogleOn = false
    @State var textFieldValue = ""

    var body: Some View {
        @State var myPlaceHolder = Textfield(
            `                   `       "Placeholder",
                                        text: $textFieldValue)
        HStack {
            Button {
                isToggleOn.Toggle()
            } label: {
                Text("Toggle")
            }
            myPlaceHolder.disabled(isToogleOn)
        }
    }
}
Protector answered 13/5, 2024 at 17:56 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.