【问题标题】:SwiftUI Modify TextField Format Dynamically, String Pattern, MaskSwiftUI动态修改TextField格式、字符串模式、掩码
【发布时间】:2020-03-16 17:41:47
【问题描述】:

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

我已经能够使用通过按钮操作的 func 来做到这一点,但当然 我希望它是自动的。我对 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)

【问题讨论】:

  • 你可以像here这样使用Binding

标签: xcode swiftui


【解决方案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

【讨论】:

  • 是的,确实如此。我不敢相信我找了这么久却找不到这个功能。它非常适合我。
  • 对于其他人 - 额外的好处。在 onEditingChange Bool 上使用 if-else,您可以在单击字段时清除 TextField 而无需进一步的代码。
  • 你可以像here这样使用Binding
【解决方案2】:

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) -&gt; 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你可以找到一篇关于这个主题的非常有趣的文章。

【讨论】:

  • 真的很酷。对于其他人-我不知道可用的重载,包括您提到的值和格式化程序。此外,从引用的文章中 - 您可以在编辑和提交字段时内联启动闭包。强烈推荐参考文章。
猜你喜欢
  • 2020-07-08
  • 1970-01-01
  • 2021-12-09
  • 2013-08-13
  • 1970-01-01
  • 1970-01-01
  • 2016-09-15
  • 2022-11-09
  • 1970-01-01
相关资源
最近更新 更多