SwiftUI 动态修改文本字段格式、字符串模式、掩码

use*_*617 4 xcode swiftui

我正在尝试使用模式或掩码格式化 SwiftUI TextField 中的数据。(为清楚起见 - 不是 UITextField)。一个例子是美国的电话号码。因此,用户可以键入 1115551212,视图中的结果为 111-555-1212。在这种情况下,我将使用数字键盘,但为了将来,使用全键盘,如果用户键入非数字,我希望能够用某些内容替换它,例如 0。因此输入 111abc1212 将导致111-000-1212。尽管下面的代码将字符串转换为数字,但理想情况下,我希望掩码对字符串而不是数字进行操作 - 从而可以灵活地格式化零件编号等。

我已经能够使用通过按钮操作的函数来做到这一点,但当然我希望它是自动的。我使用 SwiftUI 修饰符来完成同样的事情完全不成功。在我的测试中,使用内置的 .textContentType(.telephoneNumber) 绝对没有任何作用。

我希望有一些像 .onExitCommand 这样的修饰符可以在焦点离开 TextField 时执行,但我没有看到解决方案。

此代码适用于该按钮(稍后我将添加规则以在需要数字时过滤数字):

struct ContentView: View {

    @State private var phoneNumber = ""
    @State private var digitArray = [1]

    var body: some View {

        VStack {
            Group {//group one
                Text("Phone Number with Format").font(.title)
                    .padding(.top, 40)
            
                TextField("enter phone number", text: $phoneNumber)
                    .textFieldStyle(RoundedBorderTextFieldStyle())
                    .padding()
                    .keyboardType(.numberPad)

            }//Group one
        
            Group {//group two
                            
                Button(action: {self.populateTextFieldWithPhoneFormat(phoneString: self.phoneNumber)}) {
                    Text("Convert to Phone Format")
                }
            
            }//group two
            Spacer()
        }//VStack
    
    }

    func populateTextFieldWithPhoneFormat(phoneString: String) {

        var digitArray = [Int]()

        let padded = phoneNumber.padding(toLength: 10, withPad: "0", startingAt: 0)
        let paddedArray = padded.map {String($0)}

        for char in paddedArray {
            digitArray.append(Int(char) ?? 0)
        }

        phoneNumber = format(digits: digitArray)

    }//populate

    func format(digits: [Int]) -> String {
        var phone = digits.map(String.init)
            .joined()
        if digits.count > 3 {
            phone.insert("-", at: phone.index(
                phone.startIndex,
                offsetBy: 3)
            )
        }
        if digits.count > 7 {
            phone.insert("-", at: phone.index(
                phone.startIndex,
                offsetBy: 7)
            )
        }
        return phone
    }
}
Run Code Online (Sandbox Code Playgroud)

我也尝试制作自己的 ViewModifier,但一事无成。

Xcode 版本 11.2.1 (11B500)

val*_*ine 6

SwiftUI 上的 TextField 有一些有趣的重载,可以轻松解决您的问题。我设法为 MAC 地址格式化程序制作了一个 TextField 掩码,如下所示(##:##:##:##:##)。下面是一个简单的代码片段,可以实现这个目的:

class MacAddressFormatter: Formatter {
    override func string(for obj: Any?) -> String? {
        if let string = obj as? String {
            return formattedAddress(mac: string)
        }
        return nil
    }
    
    override func getObjectValue(_ obj: AutoreleasingUnsafeMutablePointer<AnyObject?>?, for string: String, errorDescription error: AutoreleasingUnsafeMutablePointer<NSString?>?) -> Bool {
        obj?.pointee = string as AnyObject?
        return true
    }
    
    func formattedAddress(mac: String?) -> String? {
        guard let number = mac else { return nil }
        let mask = "##:##:##"
        var result = ""
        var index = number.startIndex
        for ch in mask where index < number.endIndex {
            if ch == "#" {
                result.append(number[index])
                index = number.index(after: index)
            } else {
                result.append(ch)
            }
        }
        return result
    }
}
Run Code Online (Sandbox Code Playgroud)

在您的情况下,您应该更改formattedAddress(mac: String) -> String?方法以便根据需要返回结果。

下面是上述内容的简单 SwiftUI 实现:

struct ContentView: View {
    @State private var textFieldValue:String = ""
    var body: some View {
        NavigationView {
            List {
                TextField("First 6 bytes", value: $textFieldValue, formatter: MacAddressFormatter())
            }
            .navigationBarTitle("Mac Lookup", displayMode: .inline)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

对于更好奇的人: 在这里您可以找到一篇关于这个主题的非常有趣的文章。