SwiftUI – TextField – 聚焦时清除 Double 类型的值,失去焦点时恢复

huangapple go评论65阅读模式
英文:

SwiftUI - TextField - Clear value of type Double on focus, restore on deselect

问题

场景:
SwiftUI中,我的TextField具有Double值。在轻触/聚焦TextField时,当前的Double值被清除,以便用户可以轻松输入新值,而不必选择并删除当前的值。如果用户不输入新值,则在取消选择时会恢复旧值。

问题

使用.focused().onChange(of:)似乎无法实现此功能。下面的代码正确清除值(在控制台中可见)。然而,UI中未反映出更改。通过在TextField上使用.id()强制更新UI将取消选择TextField,使此解决方法无效。

为什么UI不会从对TextField值属性的更改中更新?

struct ContentView: View {
    
    @FocusState var focus: Bool
    @State var test: Double? = 100
    @State var id: UUID = UUID()
    
    var body: some View {
        VStack {
            TextField("Placeholder", value: $test, format: .number)
                .id(id)
                .focused($focus)
                .onChange(of: focus, perform: { editing in
                    var oldValue: Double? = test
                    if editing {
                        self.$test.wrappedValue = nil
                        self.$id.wrappedValue = UUID()
                    } else {
                        if test == nil {
                            self.$test.wrappedValue = oldValue
                            self.$id.wrappedValue = UUID()
                        }
                    }
                })
        }
        .padding()
    }
}

是否有一种方法可以在SwiftUITextField中实现这个功能?

英文:

Scenario:
In SwiftUI, my TextField has a Double value. On tapping/focusing the TextField the current Double value is cleared, to allow the user to easily enter a new value without having to select and delete the current value. If the user does not enter a new value, the old value is restored on deselect.

Issue

Utilising .focused() and .onChange(of:) this does not appear to be possible. Below code correctly clears the value (visible in console). However, the change is not reflected within the UI. Forcing the UI to update by utilising .id() on the TextField deselects the TextField, making this workaround useless.

Why does the UI not update from the change to the TextField value property?

struct ContentView: View {
    
    @FocusState var focus: Bool
    @State var test: Double? = 100
    @State var id: UUID = UUID()
    
    var body: some View {
        VStack {
            TextField("Placeholder", value: $test, format: .number)
                .id(id)
                .focused($focus)
                .onChange(of: focus, perform: { editing in
                    var oldValue: Double? = test
                    if editing {
                        self.$test.wrappedValue = nil
                        self.$id.wrappedValue = UUID()
                    } else {
                        if test == nil {
                            self.$test.wrappedValue = oldValue
                            self.$id.wrappedValue = UUID()
                        }
                    }
                })
        }
        .padding()
    }
}

Is there a way to do this with SwiftUI's TextField?

答案1

得分: 2

Because it doesn't bind TextField's value (String) to your value (Double) directly.

To clear value on focus, restore on deselect, you need to do it on TextField with text, not value. Here's an example:

struct TestView: View {
    @FocusState var focus: Bool
    @State var value: String = "100"
    @State private var oldValue: String = ""

    var body: some View {
        VStack {
            TextField("", text: .constant("other"))
            TextField("Placeholder", text: $value)
                .focused($focus)
                .onChange(of: focus) { editing in
                    if editing {
                        if !value.isEmpty {
                            oldValue = value
                            value = ""
                        }
                    } else {
                        if value is empty {
                            value = oldValue
                        }
                    }
                }
        }
        .textFieldStyle(.roundedBorder)
        .padding()
    }
}

If you want to make it Double only, handle it outside (in ViewModel or something)

英文:

Why UI not update from the change to the TextField value ?

Because it doesn't bind TextField's value (String) to your value (Double) directly.

To clear value on focus, restore on deselect, you need to do it on TextField with text, not value. Here's an example:

struct TestView: View {
    @FocusState var focus: Bool
    @State var value: String = "100"
    @State private var oldValue: String = ""
    
    var body: some View {
        VStack {
            TextField("", text: .constant("other"))
            TextField("Placeholder", text: $value)
                .focused($focus)
                .onChange(of: focus) { editing in
                    if editing {
                        if !value.isEmpty {
                            oldValue = value
                            value = ""
                        }
                    } else {
                        if value.isEmpty {
                            value = oldValue
                        }
                    }
                }
        }
        .textFieldStyle(.roundedBorder)
        .padding()
    }
}

If you want to make it Double only, handle it outside (in ViewModel or something)

答案2

得分: 1

根据@meomeomeo的回答进行扩展,请查看下面的可重用TextField视图,以满足TextField文本为Double类型的需求。

第二个.onChange(of:) 用于在用户输入时更新输入绑定,但仅在输入的文本是Double类型时。这是为了防止用户在保存之前从不取消选择TextField的情况。

struct TextFieldDoubleClearView: View {
    
    @FocusState var focus: Bool
    
    @Binding var input: Double
    
    @State var value: String = "100"
    @State private var oldValue: String = ""
    
    var body: some View {
        VStack {
            TextField("Placeholder", text: $value)
                .keyboardType(.decimalPad)
                .focused($focus)
                .onChange(of: focus, perform: { editing in
                    if editing {
                        if !value.isEmpty {
                            oldValue = value
                            value = ""
                        }
                    } else {
                        if value.isEmpty {
                            value = oldValue
                        } else {
                            if let valueDouble: Double = Double(value) {
                                input = valueDouble
                            } else {
                                value = oldValue
                            }
                        }
                    }
                })
                .onChange(of: value, perform: { _ in
                    if let valueDouble: Double = Double(value) {
                        input = valueDouble
                    }
                })
        }
        .padding()
        .onAppear {
            value = String(input)
        }
    }
}
英文:

Extending on @meomeomeo' answer, please find a reusable TextField view below that satisfies the need for the TextField text to be of type Double.

The second .onChange(of:) updates the input binding as the user is typing but only if the typed text is of type Double. This is in case the user never deselects the TextField before e.g. saving.

struct TextFieldDoubleClearView: View {
    
    @FocusState var focus: Bool
    
    @Binding var input: Double
    
    @State var value: String = "100"
    @State private var oldValue: String = ""
    
    var body: some View {
        VStack {
            TextField("Placeholder", text: $value)
                .keyboardType(.decimalPad)
                .focused($focus)
                .onChange(of: focus, perform: { editing in
                    if editing {
                        if !value.isEmpty {
                            oldValue = value
                            value = ""
                        }
                    } else {
                        if value.isEmpty {
                            value = oldValue
                        } else {
                            if let valueDouble: Double = Double(value) {
                                input = valueDouble
                            } else {
                                value = oldValue
                            }
                        }
                    }
                })
                .onChange(of: value, perform: { _ in
                    if let valueDouble: Double = Double(value) {
                        input = valueDouble
                    }
                })
        }
        .padding()
        .onAppear {
            value = String(input)
        }
    }
}   

huangapple
  • 本文由 发表于 2023年2月6日 07:08:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/75356128.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定