Skip to content

Instantly share code, notes, and snippets.

@sweetmandm
Last active October 24, 2017 19:50
Show Gist options
  • Save sweetmandm/526bd3e9fe01944dfdd6fbd179b7d472 to your computer and use it in GitHub Desktop.
Save sweetmandm/526bd3e9fe01944dfdd6fbd179b7d472 to your computer and use it in GitHub Desktop.
Simple Phone Number Formatting for String + UITextField
// Usage:
// ------
// /* add the action somewhere: */
// phoneTextField.addTarget(self, action: #selector(updatePhoneFormat), for: .editingChanged)
// /* implement the update action: */
// func updatePhoneFormat() { phoneTextField.formatPhoneNumber() }
import UIKit
extension String {
var phoneFormat: String {
/**
* NOTE: This assumes US country code.
*/
var text = trimNonNumbers()
let length = text.lengthOfBytes(using: .utf8)
switch length {
case 11 where text.characters.first == "1":
// example: `1 (222) 333 4444`
text.insert(" ", at: text.index(text.startIndex, offsetBy: 7))
text.insert(contentsOf: ") ".characters, at: text.index(text.startIndex, offsetBy: 4))
text.insert(contentsOf: " (".characters, at: text.index(text.startIndex, offsetBy: 1))
case 8...10 where text.characters.first != "1":
// example: `(222) 333 4444`
text.insert(contentsOf: " ".characters, at: text.index(text.startIndex, offsetBy: 6))
text.insert(contentsOf: ") ".characters, at: text.index(text.startIndex, offsetBy: 3))
text.insert(contentsOf: "(".characters, at: text.index(text.startIndex, offsetBy: 0))
default:
break
}
return text
}
func trimNonNumbers() -> String {
return components(separatedBy: CharacterSet.decimalDigits.inverted).joined()
}
}
extension UITextField {
func formatPhoneNumber() {
/**
* Note: This dance is to keep the cursor position in the same relative spot
* while editing characters in the middle of the phone number.
*/
let pos = selectedTextRange?.start
let range = textRange(from: beginningOfDocument, to: pos ?? endOfDocument)
let diffBefore: Int = {
guard let range = range else { return 0 }
let leftOfCursorLength = text(in: range)?.lengthOfBytes(using: .utf8) ?? 0
let leftOfCursorTrimmed = text(in: range)?.trimNonNumbers().lengthOfBytes(using: .utf8) ?? 0
return leftOfCursorTrimmed - leftOfCursorLength
}()
text = text?.phoneFormat
let diffAfter: Int = {
guard let range = range else { return 0 }
let leftOfCursorLength = text(in: range)?.lengthOfBytes(using: .utf8) ?? 0
let leftOfCursorTrimmed = text(in: range)?.trimNonNumbers().lengthOfBytes(using: .utf8) ?? 0
let temp = leftOfCursorTrimmed - leftOfCursorLength
guard
let pos = pos,
let newPos = position(from: pos, offset: -temp),
let newRange = textRange(from: beginningOfDocument, to: newPos)
else {
return temp
}
let newLeftOfCursorLength = text(in: newRange)?.lengthOfBytes(using: .utf8) ?? 0
let newLeftOfCursorTrimmed = text(in: newRange)?.trimNonNumbers().lengthOfBytes(using: .utf8) ?? 0
return newLeftOfCursorTrimmed - newLeftOfCursorLength
}()
if let oldPos = pos, let newPos = position(from: oldPos, offset: diffBefore - diffAfter) {
selectedTextRange = textRange(from: newPos, to: newPos)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment