Formatting Phone number in Swift

前端 未结 12 1862
天命终不由人
天命终不由人 2020-12-02 09:36

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

相关标签:
12条回答
  • 2020-12-02 09:54

    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/

    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:

    • The UITextFieldDelegate function
        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
    }
    
    0 讨论(0)
  • 2020-12-02 09:56

    You can use this library https://github.com/luximetr/AnyFormatKit

    Example

    let textInputController = TextInputController()
    
    let textInput = TextInputField() // or TextInputView or any TextInput
    textInputController.textInput = textInput // setting textInput
    
    let formatter = TextInputFormatter(textPattern: "### (###) ###-##-##", prefix: "+12")
    textInputController.formatter = formatter // setting formatter
    

    Just set your textField to this textInputController and it will format text with pattern, that you set.

    0 讨论(0)
  • 2020-12-02 09:57

    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"
    
    0 讨论(0)
  • 2020-12-02 10:03

    Swift 5.1 Update on Дарія Прокопович great solution

    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: "#")
    
    0 讨论(0)
  • 2020-12-02 10:04

    Manipulations with characters in String are not very straightforward. You need following:

    Swift 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

    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 讨论(0)
  • 2020-12-02 10:07

    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:

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

    Here is how it works as you type:

    I also set it up so you can just import it with cocoapods.

    pod 'SwiftPhoneFormat', '1.0.0'
    

    Then use it:

    import SwiftPhoneFormat
    
    var formatter = PhoneFormatter(rulesets: PNFormatRuleset.usParethesis())
    let formatted = formatter.format(number: numberString)
    
    0 讨论(0)
提交回复
热议问题