清除绑定文本到文本字段仍然显示文本字段中的文本

Clearing Binding text to textfield still shows text in textfield

我有一个 textField,旁边有一个清除文本的按钮。我的问题是 TextField View 只带有活页夹。因此,文本字段中的实际文本不会被删除,因为绑定不会重新加载视图。

我试图在示例中尽可能缩小范围:

 struct ContentView: View {
    
    @State var presentedView = false
    @State var text: String = ""
    
    var body: some View {
        Button("Hello, world!") { presentedView = true }
            .padding()
            .sheet(isPresented: $presentedView, content: {
                SomeView(viewModel: .init(textBinder: $text), text: $text)
            })
    }
}

struct SomeView: View {
    
    let viewModel: ViewModel
    @Binding var text: String
    
    var body: some View {
        HStack {
            TextFieldTyped(text: $text)
            Spacer()
            Button("erase") {
                $text.wrappedValue = ""
            }
        }
    }
    
    struct ViewModel {
        let textBinder: Binding<String>
    }
}

struct TextFieldTyped: UIViewRepresentable {
    @Binding var text: String

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

    func updateUIView(_ uiView: UITextField, context: Context) {
        uiView.becomeFirstResponder()
    }

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    class Coordinator: NSObject, UITextFieldDelegate {
        var parent: TextFieldTyped

        init(_ textField: TextFieldTyped) {
            self.parent = textField
        }
        
        func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
            if (textField.text?.count ?? 0) == 1, string.isEmpty {
                parent.text = string
            } else if string.isEmpty, !(textField.text ?? "").isEmpty {
                parent.text = String(textField.text?.dropLast() ?? "")
            } else {
                parent.text = (textField.text ?? "").isEmpty ? string : (textField.text ?? "") + string
            }
            return true
        }
    }
}

尝试下面的代码去掉无用的 ViewModel,并在 updateUIView 中更新 text

struct ContentView: View {
    @State var presentedView = false
    @State var text: String = ""
    
    var body: some View {
        Button("Hello, world!") { presentedView = true }
        .padding()
        .sheet(isPresented: $presentedView, content: {
            SomeView(text: $text)
        })
    }
}

struct SomeView: View {
    @Binding var text: String
    
    var body: some View {
        HStack {
            TextFieldTyped(text: $text)
            Spacer()
            Button("erase") {
                text = ""  // <--- here
            }
        }
    }
}

struct TextFieldTyped: UIViewRepresentable {
    @Binding var text: String
    
    func makeUIView(context: Context) -> UITextField {
        let textField = UITextField(frame: .zero)
        textField.delegate = context.coordinator
        return textField
    }
    
    func updateUIView(_ uiView: UITextField, context: Context) {
        uiView.becomeFirstResponder()
        uiView.text = text  // <--- here
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    class Coordinator: NSObject, UITextFieldDelegate {
        var parent: TextFieldTyped

        init(_ textField: TextFieldTyped) {
            self.parent = textField
        }
        
        func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
            if (textField.text?.count ?? 0) == 1, string.isEmpty {
                parent.text = string
            } else if string.isEmpty, !(textField.text ?? "").isEmpty {
                parent.text = String(textField.text?.dropLast() ?? "")
            } else {
                parent.text = (textField.text ?? "").isEmpty ? string : (textField.text ?? "") + string
            }
            return true
        }
    }
}

另一个答案非常好,但将相关变量拉出到它们自己的结构中也是个好主意,如 Data Essentials in SwiftUI (WWDC20) at 4:00 中的建议。这是我认为在您的情况下可以实现的方式:

struct SomeViewConfig {
    var presented = false
    var text: String = ""
    
    mutating func present() {
        presented = true
        text = ""
    }
}

struct ContentView2: View {
    @State var someViewConfig = SomeViewConfig()
    
    var body: some View {
        Button("Hello, world!") { someViewConfig.present() }
        .padding()
        .sheet(isPresented: $someViewConfig.presented) {
            SomeView(config: $someViewConfig)
        }
    }
}

struct SomeView: View {
    @Binding var config: SomeViewConfig
    
    var body: some View {
        HStack {
            TextFieldTyped(text: $config.text)
            Spacer()
            Button("erase") {
                config.text = ""
            }
        }
    }
}