首页 > 解决方案 > 电话号码的自定义 Swift 格式化程序

问题描述

我需要一个简单的 Swift 电话号码格式化程序来添加到 NSTextField。

我很难理解文档和示例,特别是对于 getObjectValue(_:, for:, errorDescription) -> Bool 函数的 obj?.pointee 用法。AutoreleasingUnsafeMutablePointer 的概念?从文档中看很难,我在网上找不到太多关于自定义 Swift 格式化程序的信息。我查看了 Marmel Roy 的 PhoneNumberKit,但它似乎对我的需求来说太复杂了,并且没有描述如何直接添加到格式化程序。

override func getObjectValue(_ obj: AutoreleasingUnsafeMutablePointer<AnyObject?>?, for string: String, errorDescription error: AutoreleasingUnsafeMutablePointer<NSString?>?) -> Bool {

      obj?.pointee = someConversionToInternalString(typedString)
        return true
    }

上述失败,重复只是存储一个空字符串。

标签: swiftmacos

解决方案


经过一些工作,我想出了如何做到这一点。我希望它会节省其他人这样做的时间。我的解决方案的关键是将“对象”存储为 Cocoa 对象(即目标 C 类型);这里作为 NSString 而不是 String。这是一些完整的示例代码,可以对满足我的条件的电话号码进行(简单)格式化。

import Foundation

class PhoneFormatter: Formatter {

    override init() {
        super.init()
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
    }

    func reduceToPhoneCharacters(string: String) -> String {
        let plusSign = string.prefix(1) == "+"

        // remove any non-digit characters
        let str = string.digits
        return "\(plusSign ? "+" : "")\(str)"
    }

    func displayString(string: String) -> String? {

        // take a string of symbols and convert into a formatted phone number (US, Aust, Europe)
        if string.isBlank || !string.isPhoneCharacters { return string } // will see if it improves

        let plusSign = string.prefix(1) == "+"

        // remove any non-digit characters
        let str = string.digits

        // international, + at start, recognise only US, Australian, NZ or European
        if plusSign {
            // USA
            if str.prefix(1) == "1" {
                return "+" + str.substring(start: 0, length: 1) + " (" + str.substring(start: 1, length: 3) + ") " + str.substring(start: 4, length: 3) + "-" + str.substring(start: 7, length: 4)
            }
            // Australia
            if str.prefix(2) == "61" || str.prefix(2) == "64" {
                return "+" + str.substring(start: 0, length: 3) + "-" + str.substring(start: 3, length: 4) + "-" + str.substring(start: 7, length: 4)
            }
            // Others? eg +44-123-456789
            return "+" + str.substring(start: 0, length: 2) + "-" + str.substring(start: 3, length: 4) + "-" + str.substring(start: 7, length: 10)
        }
        // mobile if begins with 04
        if str.prefix(2) == "04" {
            return str.substring(start: 0, length: 4) + "-" + str.substring(start: 4, length: 3) + "-" + str.substring(start: 7, length: 3)
        }
        // interstate
        if str.prefix(1) == "0" {
            return str.substring(start: 0, length: 2) + "-" + str.substring(start: 2, length: 4) + "-" + str.substring(start: 6, length: 4)
        }
        // 1800 or 1300 numbers
        if str.prefix(4) == "1800" || str.prefix(4) == "1300" {
            return str.substring(start: 0, length: 1) + "-" + str.substring(start: 1, length: 3) + "-" + str.substring(start: 4, length: 3) + "-" + str.substring(start: 7, length: 4)
        }
        // else Victoria substring
        return str.substring(start: 0, length: 4) + "-" + str.substring(start: 4, length: 4)
    }

    override func string(for obj: Any?) -> String? {
        // take a string of symbols and convert into a formatted phone number (US, Aust, Europe)
        guard let str = obj as? NSString else { return nil }
        return displayString(string: str as String)
    }

    override func getObjectValue(_ obj: AutoreleasingUnsafeMutablePointer<AnyObject?>?, for string: String, errorDescription error: AutoreleasingUnsafeMutablePointer<NSString?>?) -> Bool {

        obj?.pointee = reduceToPhoneCharacters(string: string) as NSString
        return true
    }

    override func editingString(for obj: Any) -> String? {
        if let str = obj as? NSString {
            return str as String
        }
        return nil
    }

    override func isPartialStringValid(_ partialString: String, newEditingString newString: AutoreleasingUnsafeMutablePointer<NSString?>?, errorDescription error: AutoreleasingUnsafeMutablePointer<NSString?>?) -> Bool {
        return partialString.isPhoneCharacters || partialString == ""
    }
}

// separate +String.swift file for extensions
import Foundation

extension String {}

    var digits: String {
        return components(separatedBy: CharacterSet.decimalDigits.inverted).joined()
    }

    var isPhoneCharacters: Bool {
        return !isEmpty && range(of: "[^+\\d]", options: .regularExpression) == nil
    }    

    func substring(start: Int, length : Int) -> String {
        if (count > start + length) {
            let startAtIndex = index(startIndex, offsetBy: start)
            return String(self[startAtIndex...index(startAtIndex, offsetBy: length - 1)])
        }
        if count > start {
            let startAtIndex = index(startIndex, offsetBy: start)
            return String(self[startAtIndex...])
        }
        return ""
    }

}

并以编程方式使用它,例如:

        @IBOutlet weak var phoneTextField: NSTextField!
        phoneTextField.formatter = PhoneFormatter()

我对此进行了编辑以删除冗余代码并使用 editingString(for:) 函数。


推荐阅读