Bart van Kuik
Bart van Kuik

Reputation: 4862

Clearing SwiftUI TextField will not restore placeholder

I have a SwiftUI screen with three textfields. When you run the code and tap the Clear button, you'll see three completely empty textfields. Expected is that you'd see the placeholder text, but that only appears in each textfield when it receives focus (i.e. user taps inside the field).

class UserInput: ObservableObject {
    @Published var text1 = "some text"
    @Published var text2 = "some more text"
    @Published var text3 = "and this is the final input"

    func clear() {
        self.text1 = ""
        self.text2 = ""
        self.text3 = ""
    }
}

struct ContentView: View {
    @ObservedObject var userInput = UserInput()

    var body: some View {
        Form {
            TextField("Type something in text1", text: self.$userInput.text1)
            TextField("Type something in text2", text: self.$userInput.text2)
            TextField("Type something in text3", text: self.$userInput.text3)
            Button("Clear all fields", action: self.userInput.clear)
        }
    }
}

Is there something I'm missing, or is there a workaround for this behavior?

Upvotes: 1

Views: 2489

Answers (1)

Bart van Kuik
Bart van Kuik

Reputation: 4862

I found a workaround. Basically I send a special character that the user could never type, then catch that and clear the field "locally", in the form itself. It works, and restores the placeholder as one would expect.

As workarounds go, this one is pretty ugly.

class UserInput: ObservableObject {
    static let clearCode = String.Element(Unicode.Scalar(7))
    @Published var text1 = "some text"
    @Published var text2 = "some more text"
    @Published var text3 = "and this is the final input"

    func clear() {
        self.text1 = String(Self.clearCode)
        self.text2 = String(Self.clearCode)
        self.text3 = String(Self.clearCode)
    }
}

struct ContentView: View {
    @ObservedObject var userInput = UserInput()

    var body: some View {
        Form {
            TextField("Type something in text1", text: self.$userInput.text1)
                .onReceive(self.userInput.text1.publisher) { newValue in
                    if newValue == UserInput.clearCode {
                        self.userInput.text1 = ""
                    }
                }
            TextField("Type something in text2", text: self.$userInput.text2)
                .onReceive(self.userInput.text2.publisher) { newValue in
                    if newValue == UserInput.clearCode {
                        self.userInput.text2 = ""
                    }
                }
            TextField("Type something in text3", text: self.$userInput.text3)
                .onReceive(self.userInput.text3.publisher) { newValue in
                    if newValue == UserInput.clearCode {
                        self.userInput.text3 = ""
                    }
                }
            Button("Clear all fields", action: self.userInput.clear)
        }
    }
}

I tried the following solution but that didn't provide a workaround and still leaves the placeholder cleared.

class UserInput: ObservableObject {
    let clearPublisher = PassthroughSubject<Bool, Never>()

    // ...
    func clear() {
        self.clearPublisher.send(true)
    }
}

struct ContentView: View {
            // ...
            TextField("Type something in text1", text: self.$userInput.text1)
                .onReceive(self.userInput.clearPublisher) { _ in
                    self.userInput.text1 = "" 
                }
            // ...

Upvotes: 3

Related Questions