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

时间:2019-11-20 21:50:50

标签: 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
    }
}

我也尝试制作自己的ViewModifier,但是一无所获。

任何指导将不胜感激。

Xcode版本11.2.1(11B500)

2 个答案:

答案 0 :(得分:1)

这不是您要查找的构造函数吗?

/// Creates an instance with a `Text` label generated from a title string.
///
/// - Parameters:
///     - title: The title of `self`, describing its purpose.
///     - text: The text to be displayed and edited.
///     - onEditingChanged: An `Action` that will be called when the user
///     begins editing `text` and after the user finishes editing `text`,
///     passing a `Bool` indicating whether `self` is currently being edited
///     or not.
///     - onCommit: The action to perform when the user performs an action
///     (usually the return key) while the `TextField` has focus.
public init<S>(_ title: S, text: Binding<String>, 
            onEditingChanged: @escaping (Bool) -> Void = { _ in }, 
            onCommit: @escaping () -> Void = {}) where S : StringProtocol

答案 1 :(得分:1)

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
    }
}

在您的情况下,您应该更改 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)
        }
    }
}

对于更好奇的人: Here 您可以找到有关此主题的非常有趣的文章。