Здесь есть несколько хороших ответов, но я принял совершенно другой подход и думал, что буду делиться, если это поможет.
Чтобы начать, я разбил шаги и компоненты форматирования в свои собственные обязанности.
Формат телефонного номера обычно можно разбить на локальный, внутренний или международный формат, который зависит от длины строки.
Я определил типы:
/// Defines the three different types of formatting phone numbers use
///
/// - local: Numbers used locally.
/// - domestic: Numbers used locally including area codes.
/// - international: Numbers used internationally with country codes.
public enum PhoneFormatType {
case local
case domestic
case international
}
Затем определили разделители, доступные для форматирования строки телефонного номера:
// Defines separators that are available for use in formatting
// phone number strings.
public enum PhoneFormatSeparator {
case hyphen
case plus
case space
case parenthesisLH
case parenthesisRH
case slash
case backslash
case pipe
case asterisk
public var value: String {
switch self {
case .hyphen: return "-"
case .plus: return "+"
case .space: return " "
case .parenthesisLH: return "("
case .parenthesisRH: return ")"
case .slash: return "/"
case .backslash: return "\\"
case .pipe: return "|"
case .asterisk: return "*"
}
}
}
Далее я определил правила форматирования, которые укажите индекс (в строке номера телефона), где вставлены разделители, такие как +, - и т. д.
// defines the separators that should be inserted in a phone number string
// and the indexes where they should be applied
public protocol PhoneNumberFormatRule {
// the index in a phone number where this separator should be applied
var index: Int { get set }
// the priority in which this rule should be applied. Sorted in inverse, 0 is highest priority, higher numbers are lower priority
var priority: Int { get set }
// the separator to use at this index
var separator: PhoneFormatSeparator { get set }
}
/// Default implementation of PhoneNumberFormatRule
open class PNFormatRule: PhoneNumberFormatRule {
public var index: Int
public var priority: Int
public var separator: PhoneFormatSeparator
public init(_ index: Int, separator: PhoneFormatSeparator, priority: Int = 0) {
self.index = index
self.separator = separator
self.priority = priority
}
}
С этими определенными я создал набор правил, которые связывают правила с заданным типом формата.
/// Defines the rule sets associated with a given phone number type.
/// e.g. international/domestic/local
public protocol PhoneFormatRuleset {
/// The type of phone number formatting to which these rules apply
var type: PhoneFormatType { get set }
/// A collection of rules to apply for this phone number type.
var rules: [PhoneNumberFormatRule] { get set }
/// The maximum length a number using this format ruleset should be. (Inclusive)
var maxLength: Int { get set }
}
Со всем, что определено таким образом, вы можете быстро установить набор правил в соответствии с любым форматом, который вам нужен.
Вот пример набора правил, который определяет 3 правила для телефона с дефисным форматированием номер строки, обычно используемой в США:
// Formats phone numbers:
// .local: 123-4567
// .domestic: 123-456-7890
// .international: +1 234-567-8901
static func usHyphen() -> [PhoneFormatRuleset] {
return [
PNFormatRuleset(.local, rules: [
PNFormatRule(3, separator: .hyphen)
], maxLength: 7),
PNFormatRuleset(.domestic, rules: [
PNFormatRule(3, separator: .hyphen),
PNFormatRule(6, separator: .hyphen)
], maxLength: 10),
PNFormatRuleset(.international, rules: [
PNFormatRule(0, separator: .plus),
PNFormatRule(1, separator: .space),
PNFormatRule(4, separator: .hyphen),
PNFormatRule(7, separator: .hyphen)
], maxLength: 11)
]
}
. Тяжелый подъем логики форматирования происходит (не так):
// formats a string using the format rule provided at initialization
public func format(number: String) -> String {
// strip non numeric characters
let n = number.components(separatedBy: CharacterSet.decimalDigits.inverted).joined()
// bail if we have an empty string, or if no ruleset is defined to handle formatting
guard n.count > 0, let type = type(for: n.count), let ruleset = ruleset(for: type) else {
return n
}
// this is the string we'll return
var formatted = ""
// enumerate the numeric string
for (i,character) in n.enumerated() {
// bail if user entered more numbers than allowed for our formatting ruleset
guard i <= ruleset.maxLength else {
break
}
// if there is a separator defined to be inserted at this index then add it to the formatted string
if let separator = ruleset.separator(for: i) {
formatted+=separator
}
// now append the character
formatted+="\(character)"
}
return formatted
}
Я создал структуру с пример проекта, вы можете посмотреть здесь: https://github.com/appteur/phoneformat
Вот как он работает по мере ввода:
Я также установил его, чтобы вы могли просто импортировать его с помощью cocoapods.
pod 'SwiftPhoneFormat', '1.0.0'
Затем используйте его:
import SwiftPhoneFormat
var formatter = PhoneFormatter(rulesets: PNFormatRuleset.usParethesis())
let formatted = formatter.format(number: numberString)