简体   繁体   English

格式化 Swift 中的电话号码

[英]Formatting Phone number in Swift

I'm formatting my textfiled text once the user start typing the phone number into this format type 0 (555) 444 66 77 and it is working fine but once I get the number from the server I get it like this 05554446677 So please could you tell me how I can edit it in the same format once I get it fro the server?一旦用户开始将电话号码输入这种格式类型0 (555) 444 66 77并且它工作正常但是一旦我从服务器获得号码我就会像这样得到它05554446677所以请你告诉我如何从服务器获取它后以相同的格式对其进行编辑?

My code once I start typing:我开始输入后的代码:

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {

    if textField == phoneNumberTextField{
        var newString = (textField.text as NSString).stringByReplacingCharactersInRange(range, withString: string)
        var components = newString.componentsSeparatedByCharactersInSet(NSCharacterSet.decimalDigitCharacterSet().invertedSet)

        var decimalString = "".join(components) as NSString
        var length = decimalString.length
        var hasLeadingOne = length > 0 && decimalString.characterAtIndex(0) == (1 as unichar)

        if length == 0 || (length > 11 && !hasLeadingOne) || length > 12{
            var newLength = (textField.text as NSString).length + (string as NSString).length - range.length as Int

            return (newLength > 11) ? false : true
        }
        var index = 0 as Int
        var formattedString = NSMutableString()

        if hasLeadingOne{
            formattedString.appendString("1 ")
            index += 1
        }

        if (length - index) > 1{
            var zeroNumber = decimalString.substringWithRange(NSMakeRange(index, 1))
            formattedString.appendFormat("%@ ", zeroNumber)
            index += 1
        }
        if (length - index) > 3{
            var areaCode = decimalString.substringWithRange(NSMakeRange(index, 3))
            formattedString.appendFormat("(%@) ", areaCode)
            index += 3
        }
        if (length - index) > 3{
            var prefix = decimalString.substringWithRange(NSMakeRange(index, 3))
            formattedString.appendFormat("%@ ", prefix)
            index += 3
        }
        if (length - index) > 3{
            var prefix = decimalString.substringWithRange(NSMakeRange(index, 2))
            formattedString.appendFormat("%@ ", prefix)
            index += 2
        }

        var remainder = decimalString.substringFromIndex(index)
        formattedString.appendString(remainder)
        textField.text = formattedString as String
        return false
    }else{
        return true
    }
}

Masked number typing掩码输入

/// mask example: `+X (XXX) XXX-XXXX`
func format(with mask: String, phone: String) -> String {
    let numbers = phone.replacingOccurrences(of: "[^0-9]", with: "", options: .regularExpression)
    var result = ""
    var index = numbers.startIndex // numbers iterator

    // iterate over the mask characters until the iterator of numbers ends
    for ch in mask where index < numbers.endIndex {
        if ch == "X" {
            // mask requires a number in this place, so take the next one
            result.append(numbers[index])

            // move numbers iterator to the next index
            index = numbers.index(after: index)

        } else {
            result.append(ch) // just append a mask character
        }
    }
    return result
}

Call the above function from the UITextField delegate method:从 UITextField 委托方法调用上述函数:

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    guard let text = textField.text else { return false }
    let newString = (text as NSString).replacingCharacters(in: range, with: string)
    textField.text = format(with: "+X (XXX) XXX-XXXX", phone: newString)
    return false
}

So, that is work better.所以,这是更好的工作。

"" => ""
"0" => "+0"
"412" => "+4 (12"
"12345678901" => "+1 (234) 567-8901"
"a1_b2-c3=d4 e5&f6|g7h8" => "+1 (234) 567-8"

Really simple solution:非常简单的解决方案:

extension String {
    func applyPatternOnNumbers(pattern: String, replacementCharacter: Character) -> String {
        var pureNumber = self.replacingOccurrences( of: "[^0-9]", with: "", options: .regularExpression)
        for index in 0 ..< pattern.count {
            guard index < pureNumber.count else { return pureNumber }
            let stringIndex = String.Index(utf16Offset: index, in: pattern)
            let patternCharacter = pattern[stringIndex]
            guard patternCharacter != replacementCharacter else { continue }
            pureNumber.insert(patternCharacter, at: stringIndex)
        }
        return pureNumber
    }
}

Usage:用法:

guard let text = textField.text else { return }
textField.text = text.applyPatternOnNumbers(pattern: "+# (###) ###-####", replacmentCharacter: "#")

Swift 3 & 4斯威夫特 3 & 4

This solution removes any non-numeric characters before applying formatting.此解决方案在应用格式之前删除任何非数字字符。 It returns nil if the source phone number cannot be formatted according to assumptions.如果无法根据假设格式化源电话号码,则返回nil

Swift 4斯威夫特 4

The Swift 4 solution accounts for the deprecation of CharacterView and Sting becoming a collection of characters as the CharacterView is. Swift 4 解决方案解释了 CharacterView 的弃用,Sting 变成了 CharacterView 的字符集合。

import Foundation

func format(phoneNumber sourcePhoneNumber: String) -> String? {
    // Remove any character that is not a number
    let numbersOnly = sourcePhoneNumber.components(separatedBy: CharacterSet.decimalDigits.inverted).joined()
    let length = numbersOnly.count
    let hasLeadingOne = numbersOnly.hasPrefix("1")

    // Check for supported phone number length
    guard length == 7 || (length == 10 && !hasLeadingOne) || (length == 11 && hasLeadingOne) else {
        return nil
    }

    let hasAreaCode = (length >= 10)
    var sourceIndex = 0

    // Leading 1
    var leadingOne = ""
    if hasLeadingOne {
        leadingOne = "1 "
        sourceIndex += 1
    }

    // Area code
    var areaCode = ""
    if hasAreaCode {
        let areaCodeLength = 3
        guard let areaCodeSubstring = numbersOnly.substring(start: sourceIndex, offsetBy: areaCodeLength) else {
            return nil
        }
        areaCode = String(format: "(%@) ", areaCodeSubstring)
        sourceIndex += areaCodeLength
    }

    // Prefix, 3 characters
    let prefixLength = 3
    guard let prefix = numbersOnly.substring(start: sourceIndex, offsetBy: prefixLength) else {
        return nil
    }
    sourceIndex += prefixLength

    // Suffix, 4 characters
    let suffixLength = 4
    guard let suffix = numbersOnly.substring(start: sourceIndex, offsetBy: suffixLength) else {
        return nil
    }

    return leadingOne + areaCode + prefix + "-" + suffix
}

extension String {
    /// This method makes it easier extract a substring by character index where a character is viewed as a human-readable character (grapheme cluster).
    internal func substring(start: Int, offsetBy: Int) -> String? {
        guard let substringStartIndex = self.index(startIndex, offsetBy: start, limitedBy: endIndex) else {
            return nil
        }

        guard let substringEndIndex = self.index(startIndex, offsetBy: start + offsetBy, limitedBy: endIndex) else {
            return nil
        }

        return String(self[substringStartIndex ..< substringEndIndex])
    }
}

Swift 3斯威夫特 3

import Foundation

func format(phoneNumber sourcePhoneNumber: String) -> String? {

    // Remove any character that is not a number
    let numbersOnly = sourcePhoneNumber.components(separatedBy: CharacterSet.decimalDigits.inverted).joined()
    let length = numbersOnly.characters.count
    let hasLeadingOne = numbersOnly.hasPrefix("1")

    // Check for supported phone number length
    guard length == 7 || (length == 10 && !hasLeadingOne) || (length == 11 && hasLeadingOne) else {
        return nil
    }

    let hasAreaCode = (length >= 10)
    var sourceIndex = 0

    // Leading 1
    var leadingOne = ""
    if hasLeadingOne {
        leadingOne = "1 "
        sourceIndex += 1
    }

    // Area code
    var areaCode = ""
    if hasAreaCode {
        let areaCodeLength = 3
        guard let areaCodeSubstring = numbersOnly.characters.substring(start: sourceIndex, offsetBy: areaCodeLength) else {
            return nil
        }
        areaCode = String(format: "(%@) ", areaCodeSubstring)
        sourceIndex += areaCodeLength
    }

    // Prefix, 3 characters
    let prefixLength = 3
    guard let prefix = numbersOnly.characters.substring(start: sourceIndex, offsetBy: prefixLength) else {
        return nil
    }
    sourceIndex += prefixLength

    // Suffix, 4 characters
    let suffixLength = 4
    guard let suffix = numbersOnly.characters.substring(start: sourceIndex, offsetBy: suffixLength) else {
        return nil
    }

    return leadingOne + areaCode + prefix + "-" + suffix
}

extension String.CharacterView {
    /// This method makes it easier extract a substring by character index where a character is viewed as a human-readable character (grapheme cluster).
    internal func substring(start: Int, offsetBy: Int) -> String? {
        guard let substringStartIndex = self.index(startIndex, offsetBy: start, limitedBy: endIndex) else {
            return nil
        }

        guard let substringEndIndex = self.index(startIndex, offsetBy: start + offsetBy, limitedBy: endIndex) else {
            return nil
        }

        return String(self[substringStartIndex ..< substringEndIndex])
    }
}

Example例子

func testFormat(sourcePhoneNumber: String) -> String {
    if let formattedPhoneNumber = format(phoneNumber: sourcePhoneNumber) {
        return "'\(sourcePhoneNumber)' => '\(formattedPhoneNumber)'"
    }
    else {
        return "'\(sourcePhoneNumber)' => nil"
    }
}

print(testFormat(sourcePhoneNumber: "1 800 222 3333"))
print(testFormat(sourcePhoneNumber: "18002223333"))
print(testFormat(sourcePhoneNumber: "8002223333"))
print(testFormat(sourcePhoneNumber: "2223333"))
print(testFormat(sourcePhoneNumber: "18002223333444"))
print(testFormat(sourcePhoneNumber: "Letters8002223333"))
print(testFormat(sourcePhoneNumber: "1112223333"))

Example Output示例输出

'1 800 222 3333' => '1 (800) 222-3333'

'18002223333' => '1 (800) 222-3333'

'8002223333' => '(800) 222-3333'

'2223333' => '222-3333'

'18002223333444' => nil

'Letters8002223333' => '(800) 222-3333'

'1112223333' => nil

Manipulations with characters in String are not very straightforward.对 String 中的字符进行操作并不是很简单。 You need following:您需要以下内容:

Swift 2.1斯威夫特 2.1

let s = "05554446677"
let s2 = String(format: "%@ (%@) %@ %@ %@", s.substringToIndex(s.startIndex.advancedBy(1)),
    s.substringWithRange(s.startIndex.advancedBy(1) ... s.startIndex.advancedBy(3)),
    s.substringWithRange(s.startIndex.advancedBy(4) ... s.startIndex.advancedBy(6)),
    s.substringWithRange(s.startIndex.advancedBy(7) ... s.startIndex.advancedBy(8)),
    s.substringWithRange(s.startIndex.advancedBy(9) ... s.startIndex.advancedBy(10))
)

Swift 2.0斯威夫特 2.0

let s = "05554446677"
let s2 = String(format: "%@ (%@) %@ %@ %@", s.substringToIndex(advance(s.startIndex, 1)),
    s.substringWithRange(advance(s.startIndex, 1) ... advance(s.startIndex, 3)),
    s.substringWithRange(advance(s.startIndex, 4) ... advance(s.startIndex, 6)),
    s.substringWithRange(advance(s.startIndex, 7) ... advance(s.startIndex, 8)),
    s.substringWithRange(advance(s.startIndex, 9) ... advance(s.startIndex, 10))
)

Code will print 0 (555) 444 66 77代码将打印0 (555) 444 66 77

You can use this library https://github.com/luximetr/AnyFormatKit你可以使用这个库https://github.com/luximetr/AnyFormatKit

Example例子

let phoneFormatter = DefaultTextFormatter(textPattern: "### (###) ###-##-##")
phoneFormatter.format("+123456789012") // +12 (345) 678-90-12

Very simple to use.使用起来非常简单。

Swift 4斯威夫特 4

Create this function and call on text field event Editing Changed创建此函数并调用文本字段事件 Editing Changed

private func formatPhone(_ number: String) -> String {
    let cleanNumber = number.components(separatedBy: CharacterSet.decimalDigits.inverted).joined()
    let format: [Character] = ["X", "X", "X", "-", "X", "X", "X", "-", "X", "X", "X", "X"]

    var result = ""
    var index = cleanNumber.startIndex
    for ch in format {
        if index == cleanNumber.endIndex {
            break
        }
        if ch == "X" {
            result.append(cleanNumber[index])
            index = cleanNumber.index(after: index)
        } else {
            result.append(ch)
        }
    }
    return result
}

Swift 5.1 Update on Дарія Прокопович great solution Swift 5.1 Update on Дарія Прокопович 很好的解决方案

extension String {

    func applyPatternOnNumbers(pattern: String, replacmentCharacter: Character) -> String {
        var pureNumber = self.replacingOccurrences( of: "[^0-9]", with: "", options: .regularExpression)
        for index in 0 ..< pattern.count {
            guard index < pureNumber.count else { return pureNumber }
            let stringIndex = String.Index(utf16Offset: index, in: self)
            let patternCharacter = pattern[stringIndex]
            guard patternCharacter != replacmentCharacter else { continue }
            pureNumber.insert(patternCharacter, at: stringIndex)
        }
        return pureNumber
    }
}

Usage:用法:

let formattedText = text.applyPatternOnNumbers(pattern: "+# (###) ###-####", replacmentCharacter: "#")

Swift 3 but should also be translatable to Swift 4 Swift 3 但也应该可以翻译成 Swift 4

  1. ErrorHandling错误处理

    enum PhoneNumberFormattingError: Error { case wrongCharactersInPhoneNumber case phoneNumberLongerThanPatternAllowes }
  2. Create Patterns创建模式

    enum PhoneNumberFormattingPatterns: String { case mobile = "+xx (yxx) xxxxxxxxxxx" case home = "+xx (yxxx) xxxx-xxx" }
  3. Insert Function插入功能

    /** Formats a phone-number to correct format - Parameter pattern: The pattern to format the phone-number. - Example: - x: Says that this should be a digit. - y: Says that this digit cannot be a "0". - The length of the pattern restricts also the length of allowed phone-number digits. - phone-number: "+4306641234567" - pattern: "+xx (yxx) xxxxxxxxxxx" - result: "+43 (664) 1234567" - Throws: - PhoneNumberFormattingError - wrongCharactersInPhoneNumber: if phone-number contains other characters than digits. - phoneNumberLongerThanPatternAllowes: if phone-number is longer than pattern allows. - Returns: - The formatted phone-number due to the pattern. */ extension String { func vpToFormattedPhoneNumber(withPattern pattern: PhoneNumberFormattingPatterns) throws -> String { let phoneNumber = self.replacingOccurrences(of: "+", with: "") var retVal: String = "" var index = 0 for char in pattern.rawValue.lowercased().characters { guard index < phoneNumber.characters.count else { return retVal } if char == "x" { let charIndex = phoneNumber.index(phoneNumber.startIndex, offsetBy: index) let phoneChar = phoneNumber[charIndex] guard "0"..."9" ~= phoneChar else { throw PhoneNumberFormattingError.wrongCharactersInPhoneNumber } retVal.append(phoneChar) index += 1 } else if char == "y" { var charIndex = phoneNumber.index(phoneNumber.startIndex, offsetBy: index) var indexTemp = 1 while phoneNumber[charIndex] == "0" { charIndex = phoneNumber.index(phoneNumber.startIndex, offsetBy: index + indexTemp) indexTemp += 1 } let phoneChar = phoneNumber[charIndex] guard "0"..."9" ~= phoneChar else { throw PhoneNumberFormattingError.wrongCharactersInPhoneNumber } retVal.append(phoneChar) index += indexTemp } else { retVal.append(char) } } if phoneNumber.endIndex > phoneNumber.index(phoneNumber.startIndex, offsetBy: index) { throw PhoneNumberFormattingError.phoneNumberLongerThanPatternAllowes } return retVal } }
  4. Usage用法

    let phoneNumber = "+4306641234567" let phoneNumber2 = "4343211234567" do { print(try phoneNumber.vpToFormattedPhoneNumber(withPattern: .mobile)) print(try phoneNumber2.vpToFormattedPhoneNumber(withPattern: .home)) } catch let error as PhoneNumberFormattingError { switch error { case .wrongCharactersInPhoneNumber: print("wrong characters in phone number") case .phoneNumberLongerThanPatternAllowes: print("too long phone number") default: print("unknown error") } } catch { print("something other went wrong") } // output: +43 (664) 1234567 // output: +43 (4321) 1234-567

There are a number of good answers here but I took a completely different approach and thought I'd share in case it helps.这里有很多很好的答案,但我采取了一种完全不同的方法,并认为我会分享以防万一。

To start I broke up the formatting steps and components into their own separate responsibilities.首先,我将格式化步骤和组件分解为各自独立的职责。

Phone number format can generally be broken down into local, domestic or international format types that vary by string length.电话号码格式通常可以分解为本地、国内或国际格式类型,这些类型因字符串长度而异。

I defined the types:我定义了类型:

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

Then defined the separators available to format a phone number string:然后定义可用于格式化电话号码字符串的分隔符:

// 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 "*"
        }
    }
}

Next I defined formatting rules that specify the index (in a phone number string) where the separators like +,-,etc are inserted.接下来,我定义了格式规则,用于指定插入 +、- 等分隔符的索引(在电话号码字符串中)。

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

With these defined, I created rulesets that associate rules with a given format type.定义了这些之后,我创建了将规则与给定格式类型相关联的规则集。

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

With everything defined this way, you can setup rulesets quickly to suit whatever format you need.通过以这种方式定义所有内容,您可以快速设置规则集以适应您需要的任何格式。

Here's an example of a ruleset that defines 3 rules for a hyphen formatted phone number string typically used in the US:下面是一个规则集示例,它为通常在美国使用的连字符格式的电话号码字符串定义了 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)
        ]
    }

The (not so) heavy lifting of the formatting logic happens here:格式化逻辑的(并非如此)繁重的工作发生在这里:

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

I've created a framework with a sample project you can look through here: https://github.com/appteur/phoneformat我创建了一个带有示例项目的框架,您可以在此处查看: https : //github.com/appteur/phoneformat

Here is how it works as you type:这是您键入时的工作方式:

演示 Gif

I also set it up so you can just import it with cocoapods.我也设置了它,所以你可以用 cocoapods 导入它。

pod 'SwiftPhoneFormat', '1.0.0'

Then use it:然后使用它:

import SwiftPhoneFormat

var formatter = PhoneFormatter(rulesets: PNFormatRuleset.usParethesis())
let formatted = formatter.format(number: numberString)

This is the extension which will full fill your requirement:这是将完全满足您的要求的扩展:

 extension String {
 func convertToInternationalFormat() -> String {
    let isMoreThanTenDigit = self.count > 10
    _ = self.startIndex
    var newstr = ""
    if isMoreThanTenDigit {
        newstr = "\(self.dropFirst(self.count - 10))"
    }
    else if self.count == 10{
        newstr = "\(self)"
    }
    else {
        return "number has only \(self.count) digits"
    }
    if  newstr.count == 10 {
        let internationalString = "(\(newstr.dropLast(7))) \(newstr.dropLast(4).dropFirst(3)) \(newstr.dropFirst(6).dropLast(2)) \(newstr.dropFirst(8))"
        newstr = internationalString
    }
    return newstr
 }
 }

INPUT :
var str1 = "9253248954"
var str2 = "+19253248954"
var str3 = "19253248954"

OUTPUT :
str1.convertToInternationalFormat() // "(925) 324 89 54"
str2.convertToInternationalFormat() // "(925) 324 89 54"
str3.convertToInternationalFormat() // "(925) 324 89 54"
var formattedPhone = phone
if phone.count == 11 {
    let firstChar = phone[..<phone.index(phone.startIndex, offsetBy: 1)]
    if firstChar == "1" {
        formattedPhone = String(format: "(%@) %@-%@",
                                            String(phone[phone.index(phone.startIndex, offsetBy: 1)..<phone.index(phone.startIndex, offsetBy: 4)]),
                                            String(phone[phone.index(phone.startIndex, offsetBy: 4) ..< phone.index(phone.startIndex, offsetBy: 7)]),
                                            String(phone[phone.index(phone.startIndex, offsetBy: 7) ..< phone.index(phone.startIndex, offsetBy: 11)]))
       }
 }

If you rather to do it without using a library.如果您宁愿在不使用库的情况下执行此操作。 Here is a link to the best example or you can use the code below.这是最佳示例的链接,或者您可以使用下面的代码。

https://ivrodriguez.com/format-phone-numbers-in-swift/ https://ivrodriguez.com/format-phone-numbers-in-swift/

A simple code snippet to format 10 digit phone numbers in Swift 5.0, instead of including a big library, just implement a delegate function and a formatting function:在 Swift 5.0 中格式化 10 位电话号码的简单代码片段,而不是包含一个大库,只需实现一个委托函数和一个格式化函数:

  • The UITextFieldDelegate function UITextFieldDelegate 函数
    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    var fullString = textField.text ?? ""
    fullString.append(string)
    if range.length == 1 {
        textField.text = format(phoneNumber: fullString, shouldRemoveLastDigit: true)
    } else {
        textField.text = format(phoneNumber: fullString)
    }
    return false
}
  • The formatting function:格式化功能:
func format(phoneNumber: String, shouldRemoveLastDigit: Bool = false) -> String {
    guard !phoneNumber.isEmpty else { return "" }
    guard let regex = try? NSRegularExpression(pattern: "[\\s-\\(\\)]", options: .caseInsensitive) else { return "" }
    let r = NSString(string: phoneNumber).range(of: phoneNumber)
    var number = regex.stringByReplacingMatches(in: phoneNumber, options: .init(rawValue: 0), range: r, withTemplate: "")

    if number.count > 10 {
        let tenthDigitIndex = number.index(number.startIndex, offsetBy: 10)
        number = String(number[number.startIndex..<tenthDigitIndex])
    }

    if shouldRemoveLastDigit {
        let end = number.index(number.startIndex, offsetBy: number.count-1)
        number = String(number[number.startIndex..<end])
    }

    if number.count < 7 {
        let end = number.index(number.startIndex, offsetBy: number.count)
        let range = number.startIndex..<end
        number = number.replacingOccurrences(of: "(\\d{3})(\\d+)", with: "($1) $2", options: .regularExpression, range: range)

    } else {
        let end = number.index(number.startIndex, offsetBy: number.count)
        let range = number.startIndex..<end
        number = number.replacingOccurrences(of: "(\\d{3})(\\d{3})(\\d+)", with: "($1) $2-$3", options: .regularExpression, range: range)
    }

    return number
}

SwiftUI code for mobile number formatting textfield SwiftUI 手机号码格式化文本字段代码

struct MobileNumberTextFieldContainer: UIViewRepresentable {
private var placeholder : String
private var text : Binding<String>

init(_ placeholder:String, text:Binding<String>) {
    self.placeholder = placeholder
    self.text = text
}

func makeCoordinator() -> MobileNumberTextFieldContainer.Coordinator {
    Coordinator(self)
}

func makeUIView(context: UIViewRepresentableContext<MobileNumberTextFieldContainer>) -> UITextField {

    let innertTextField = UITextField(frame: .zero)
    innertTextField.placeholder = placeholder
    innertTextField.text = text.wrappedValue
    innertTextField.delegate = context.coordinator

    context.coordinator.setup(innertTextField)

    return innertTextField
}

func updateUIView(_ uiView: UITextField, context: UIViewRepresentableContext<MobileNumberTextFieldContainer>) {
    uiView.text = self.text.wrappedValue
}

class Coordinator: NSObject, UITextFieldDelegate {
    var parent: MobileNumberTextFieldContainer

    init(_ textFieldContainer: MobileNumberTextFieldContainer) {
        self.parent = textFieldContainer
    }

    func setup(_ textField:UITextField) {
        textField.addTarget(self, action: #selector(textFieldDidChange), for: .editingChanged)
    }

    @objc func textFieldDidChange(_ textField: UITextField) {
        var isCursorLast = false
        var cursorPosition = 0
        
        let textLenght =  textField.text?.count ?? 0
        if let selectedRange = textField.selectedTextRange {
            
            cursorPosition = textField.offset(from: textField.beginningOfDocument, to: selectedRange.start)
            
            print("\(cursorPosition) lengh = \(textLenght)")
            
            if cursorPosition < textLenght {
                isCursorLast = true
            }
        }
        
        textField.text = textField.text?.applyPatternOnNumbers(pattern: "+# (###) ###-####", replacementCharacter: "#")
        //textField.text = textField.text ?? "".format(phoneNumber: textField.text ?? "")
        self.parent.text.wrappedValue = textField.text ?? ""
        
        if isCursorLast {
            isCursorLast = false
            
            let arbitraryValue: Int = cursorPosition
            if let newPosition = textField.position(from: textField.beginningOfDocument, offset: arbitraryValue) {

                textField.selectedTextRange = textField.textRange(from: newPosition, to: newPosition)
            }
        }
    }
}

} }

I have use the same formatting function which is @Mark Wilson used And simply we can add this in our view我使用了与@Mark Wilson 相同的格式 function 我们只需将其添加到我们的视图中

MobileNumberTextFieldContainer("Phone Number", text: $phoneNumber)

SwiftUI SwiftUI

My answer tweaks and builds on Mobile Dan's answer and adapts it for a SwiftUI TextField.我的答案基于Mobile Dan 的答案进行了调整和构建,并将其调整为 SwiftUI TextField。 If formatting fails or it's less than 10 numbers, it will return the unformatted string.如果格式化失败或小于 10 个数字,它将返回未格式化的字符串。 This works with the phone number suggestion feature, assuming a one digit country code.这适用于电话号码建议功能,假设是一位数字的国家/地区代码。 Should be easy to adapt for multi-digit country codes.应该很容易适应多位数的国家代码。

TextField("Phone", text: $phoneNumber)
    .keyboardType(.numberPad)
    .textContentType(.telephoneNumber)
    .onChange(of: phoneNumber) { _ in
        phoneNumber = phoneNumber.formatPhoneNumber()
    }

String Extensions:字符串扩展:

extension String {
    func formatPhoneNumber() -> String {
        // Remove any character that is not a number
        let numbersOnly = self.components(separatedBy: CharacterSet.decimalDigits.inverted).joined()
        let length = numbersOnly.count
    
        // Check for supported phone number length
        if length > 11 {
            return String(numbersOnly.prefix(11)).formatPhoneNumber()
        } else if length < 10 {
            return numbersOnly
        }
    
        var sourceIndex = 0
    
        // Leading Number
        var leadingNumber = ""
        if length == 11, let leadChar = numbersOnly.first {
            leadingNumber = String(leadChar) + " "
            sourceIndex += 1
        }
    
        // Area code
        var areaCode = ""
        let areaCodeLength = 3
        guard let areaCodeSubstring = numbersOnly.substring(start: sourceIndex, offsetBy: areaCodeLength) else {
            return numbersOnly
        }
        areaCode = String(format: "(%@) ", areaCodeSubstring)
        sourceIndex += areaCodeLength
    
        // Prefix, 3 characters
        let prefixLength = 3
        guard let prefix = numbersOnly.substring(start: sourceIndex, offsetBy: prefixLength) else {
            return numbersOnly
        }
        sourceIndex += prefixLength
    
        // Suffix, 4 characters
        let suffixLength = 4
        guard let suffix = numbersOnly.substring(start: sourceIndex, offsetBy: suffixLength) else {
            return numbersOnly
        }
    
        return leadingNumber + areaCode + prefix + "-" + suffix
    }
}

extension String {
    func substring(start: Int, offsetBy: Int) -> String? {
        guard let substringStartIndex = self.index(startIndex, offsetBy: start, limitedBy: endIndex) else {
            return nil
        }
    
        guard let substringEndIndex = self.index(startIndex, offsetBy: start + offsetBy, limitedBy: endIndex) else {
            return nil
        }
    
        return String(self[substringStartIndex ..< substringEndIndex])
    }
}

Swift 5斯威夫特 5

String(
    format: "(%@) %@-%@",
    rawNumber.subString(from: 0, to: 2),
    rawNumber.subString(from: 3, to: 5),
    rawNumber.subString(from: 6, to: 9)
)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM