После некоторой работы я решил, как это сделать. Я надеюсь, что это сэкономит время других на этом. Ключ в моем решении состоял в том, чтобы сохранить «объект» как объект Какао (то есть объективный тип C);здесь как строка NSString, а не строка. Вот некоторый полный пример кода, который выполняет (простое) форматирование телефонных номеров, подходящих для моих условий.
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()
Я редактировалэто для удаления избыточного кода и использования функции editString (для :).