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?
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
}
}
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
Masked number typing
private func formattedNumber(number: String) -> String {
let cleanPhoneNumber = number.components(separatedBy: CharacterSet.decimalDigits.inverted).joined()
let mask = "+X (XXX) XXX-XXXX"
var result = ""
var index = cleanPhoneNumber.startIndex
for ch in mask where index < cleanPhoneNumber.endIndex {
if ch == "X" {
result.append(cleanPhoneNumber[index])
index = cleanPhoneNumber.index(after: index)
} else {
result.append(ch)
}
}
return result
}
Call the above function from the UITextField delegate method:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let newString = (textField.text! as NSString).replacingCharacters(in: range, with: string)
textField.text = formattedNumber(number: newString)
return false
}
So, that is work better.
"" => ""
"0" => "+0"
"412" => "+4 (12"
"12345678901" => "+1 (234) 567-8901"
Swift 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.
Swift 4
The Swift 4 solution accounts for the deprecation of CharacterView and Sting becoming a collection of characters as the CharacterView is.
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 || (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
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 || (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"))
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'
Really simple 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(encodedOffset: index)
let patternCharacter = pattern[stringIndex]
guard patternCharacter != replacmentCharacter else { continue }
pureNumber.insert(patternCharacter, at: stringIndex)
}
return pureNumber
}
}
Usage:
guard let text = textField.text else { return }
textField.text = text.applyPatternOnNumbers(pattern: "+# (###) ###-####", replacmentCharacter: "#")
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.
Swift 3 but should also be translatable to Swift 4
ErrorHandling
enum PhoneNumberFormattingError: Error { case wrongCharactersInPhoneNumber case phoneNumberLongerThanPatternAllowes }
Create Patterns
enum PhoneNumberFormattingPatterns: String { case mobile = "+xx (yxx) xxxxxxxxxxx" case home = "+xx (yxxx) xxxx-xxx" }
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 } }
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
Swift 4
Create this function and call on text field event 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
}
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)
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)]))
}
}
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"
来源:https://stackoverflow.com/questions/32364055/formattting-phone-number-in-swift