SwiftUI:TextField 条件验证

SwiftUI: TextField conditional validation

用户可以使用 TextFields 提供一些数字输入。如果选择器设置为“plot”,则任何有效数字都可以,但如果设置为“map”,则验证必须将数字限制在(在本例中)-180 和 +180 之间。好数字显示绿色轮廓,坏数字显示红色。

在更改选择器之前,我可以很好地处理验证。例如1234 的输入对于“绘图”很好,但切换到“地图”时仍然显示绿色,否则就可以了。当无效的“映射”号码(显示红色)将选择器更改为“绘图”时,也是如此。无论焦点是否在特定的 TextField 上,都是如此。

我已将其缩减为下面演示问题的少量代码。我不确定我的逻辑是否错误,或者我只是没有正确实施它。任何帮助表示赞赏。

struct ContentView: View {

   @StateObject var addPatternVM = AddPatternVM()

   var body: some View {
       VStack {
           Picker(selection: $addPatternVM.type, label: Text("")) {
               Text("Plot").tag("plot")
               Text("Map").tag("map")
           }.pickerStyle(SegmentedPickerStyle())
           TextField(addPatternVM.type == "plot" ? "Minimum x" : "Minimum longitude", text: $addPatternVM.minimumXString)
               .textFieldStyle(BorderedTextView(isGood: addPatternVM.minimumXIsGood))
               .onChange(of: addPatternVM.minimumXString) { newValue in
                   addPatternVM.isMinimumXGood(newValue: newValue)
               }
       }.padding()
   }
}

我的简化视图模型:

class AddPatternVM: ObservableObject {

   @Published var type: String = "plot"
   @Published var minimumXIsGood = false
   @Published var minimumXString = ""

   func isMinimumXGood(newValue: String) {
       if type == "plot" {
           if numberIsGood(newValue: newValue) {
               minimumXIsGood = true
           } else {
               minimumXIsGood = false
           }
       } else if type == "map" {
           if longitudeIsGood(newValue: newValue) {
               minimumXIsGood = true
           } else {
               minimumXIsGood = false
           }
       }
   }

   func numberIsGood(newValue: String) -> Bool {
       return Double(newValue) != nil ? true : false
   }

   func longitudeIsGood(newValue: String) -> Bool {
       guard let longitude = Double(newValue) else { return false }
    
       if longitude < -180.0 || longitude > 180.0 {
           return false
       } else {
           return true
       }
   }
}

这只是我用来显示 valid/invalid 输入的 TextFieldStyle。

struct BorderedTextView: TextFieldStyle {

   var isGood: Bool

   public func _body(
       configuration: TextField<Self._Label>) -> some View {
       return configuration
           .padding(EdgeInsets(top: 8, leading: 16, bottom: 8, trailing: 16))
           .background(Color.white)
           .overlay(RoundedRectangle(cornerRadius: 8)
                       .stroke(lineWidth: 1)
                       .foregroundColor(isGood ? .green : .red))
   }
}

我看到的问题是,当您将选择器从绘图更改为地图时,您不会重新计算“minimumXIsGood”值,反之亦然。试试这个:

    Picker(selection: $addPatternVM.type, label: Text("")) {
        Text("Plot").tag("plot")
        Text("Map").tag("map")
    }.pickerStyle(SegmentedPickerStyle())
    .onChange(of: addPatternVM.type) { _ in
        addPatternVM.isMinimumXGood(newValue: addPatternVM.minimumXString)
    }