SwiftUI 中 <Double> 类型输入的 TextField 清除按钮

TextField clean button for inputs of type <Double> in SwiftUI

你能帮我清除按钮代码吗,它不能正常工作?

我有一个 TextField,它存储一个 Double 类型的输入,不幸的是,包含附加修饰符的经典解决方案不起作用。

这是我的代码:

import SwiftUI

struct ContentView: View {
    
    @State private var amount: Double = 10
    @FocusState private var isFocused: Bool
    
    var body: some View {
        NavigationView {
            Form {
                Section {
                    TextField("Amount to pay", value: $amount, format: .currency(code: "USD"))
                        .keyboardType(.decimalPad)
                        .focused($isFocused)
                        .modifier(TextFieldClearButton(amount: $amount, focus: $isFocused))
                }
            }
        }
        .toolbar {
            ToolbarItemGroup(placement: .keyboard) {
                
                Spacer()
                
                Button("Done") {
                    isFocused = false
                }
            }
        }
    }
}

struct TextFieldClearButton: ViewModifier {
    @Binding var amount: Double
    @FocusState.Binding var focus: Bool
    
    func body(content: Content) -> some View {
        HStack {
            content
            
            if amount != 0.0 {
                Button(
                    action: {
                        self.amount = 0.0
                        if focus == false {
                            focus = true
                        }
                    },
                    label: {
                        Image(systemName: "delete.left")
                            .foregroundColor(Color(UIColor.opaqueSeparator))
                    }
                )
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

当我调用修改器时,视图似乎没有更新,所以我只能看到默认值。我该如何解决这个问题?

我发现当 TextField 获得焦点时,您无法从外部更新该值。它只是忽略它。因此,为了让 Textfield 在聚焦时更新,您还必须引起视图刷新。最简单的方法是在 TextField 上放置一个 .id(),其值可以像这样更改:

struct ButtonClearTextField: View {
    // Your Double is now optional
    @State private var amount: Double? = 10
    @FocusState private var isFocused: Bool
    @State var updater = UUID()
    
    var body: some View {
        Form {
            Section {
                TextField("Amount to pay", value: $amount, format: .currency(code: "USD"))
                    .keyboardType(.decimalPad)
                    .focused($isFocused)
                    // Put the id in the view. I put it here as it helps indicate what is changing.
                    .id(updater)
                    // textFieldClearButton is a func on View as a result of the extension
                    .textFieldClearButton(amount: $amount, focus: $isFocused, updater: $updater)

                // I added this button so you could test that if
                // the TextField is focused it won't change.
                Button {
                    amount = 0
                } label: {
                    Text("Clear")
                }
                
            }
        }
        .toolbar {
            ToolbarItemGroup(placement: .keyboard) {
                
                Spacer()
                
                Button("Done") {
                    isFocused = false
                }
            }
        }
    }
}

struct TextFieldClearButton: ViewModifier {
    @Binding var amount: Double?
    @FocusState.Binding var focus: Bool
    @Binding var updater: UUID
    
    func body(content: Content) -> some View {
        HStack {
            content
            
            if amount != 0.0 {
                Button(
                    action: {
                        // Setting to nil clears the field
                        amount = nil
                        // After setting the value, 
                        updater = UUID()
                        DispatchQueue.main.async {
                            focus = true
                        }
                    },
                    label: {
                        Image(systemName: "delete.left")
                            .foregroundColor(Color(UIColor.opaqueSeparator))
                    }
                )
            }
        }
    }
}

// Creates func textFieldClearButton
extension View {
    func textFieldClearButton(amount: Binding<Double?>, focus: FocusState<Bool>.Binding, updater: Binding<UUID>) -> some View {
        self.modifier(TextFieldClearButton(amount: amount, focus: focus, updater: updater))
    }
}

为了获得 Asperi 链接到的行为,您只需使用一个可选值并将其设置为 nil。

我还做了一个 View 扩展来稍微清理一下调用站点的代码。