在 iOS 中以电话号码格式 XXX-XXX-XXXX 自动格式化 UITextfield



我正在尝试以 XXX-XXX-XXXX 格式自动格式化我的文本字段。规则是它应该采用上述格式,第一个数字应该大于零,最多应该为 10 位数字,我的函数中已经添加了正则表达式。以下是我正在使用的方法

@IBAction func validateAction(_ sender: Any) {
guard let phoneNumber = phoneNumber.text else {return }
if validatePhoneNumber(phoneNumber: phoneNumber) {
errorMessage.text = "Validation successful"
} else {
errorMessage.text = "Validation failed"
}
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
guard let currentText = textField.text as NSString? else {return true}
let textString = currentText.replacingCharacters(in: range, with: string)
if textField == phoneNumber {
return textField.updatePhoneNumber(string, textString)
}else{
return true
}
}
func validatePhoneNumber(phoneNumber: String) -> Bool {
let phoneRegex: String = "^[2-9]\d{2}-\d{3}-\d{4}$"
return NSPredicate(format: "SELF MATCHES %@", phoneRegex).evaluate(with: phoneNumber)
}
extension UITextField {
func updatePhoneNumber(_ replacementString: String?, _ textString: String?) -> Bool {
guard let textCount = textString?.count else {return true}
guard let currentString = self.text else {return true}
if replacementString == "" {
return true
} else if textCount == 4 {
self.text =  currentString + "-"
} else if textCount == 8 {
self.text = currentString + "-"
} else if textCount > 12 || replacementString == " " {
return false
}
return true
}
}

这在某种程度上有效,现在的问题是,用户可以手动干预并破坏格式,例如:如果我输入 234-567-8990,用户可以将光标放在 5 和退格键之前,并在末尾或之间键入 567-89900000 或 234567-8990。通过验证正则表达式,它会给出错误,但我想在用户输入时重新调整格式。例如:在早期的场景中,如果用户在 5 之前使用光标并退格,则不应删除破折号 (-(,而只是删除 4 并重新调整格式,如 235-678-990。有什么简单的方法可以做到这一点吗?任何帮助不胜感激

我将此扩展用于字符串。它很小,真的很有帮助。

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
}

只需设置所需的面具

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

仅此而已

@SonuP非常好的问题。我相信您想格式化手机并将光标保持在正确的位置。如果是这样,那么此任务比仅格式化稍微复杂一些。您需要重新格式化代码并更新光标位置。 请注意,我的解决方案遵循特定的格式,如果它与您的不匹配,请稍微调整代码:

斯威夫特 5

public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
var text = textField.text ?? ""
text.replaceSubrange(range.toRange(string: text), with: string)
if let phone = (textField.text ?? "").replacePhoneSubrange(range, with: string) {
// update text in the field
textField.text = text
// update cursor position
if text.count == range.location + string.count || text.hasSuffix(")") && text.count == range.location + string.count + 1 { // end
if phone.hasSuffix(")") {
textField.setCursor(phone.count - 1)
}
else {
textField.setCursor(phone.count)
}
}
else {
textField.setCursor(min(range.location + string.count, phone.count-1))
}
}
return false
}

您还需要以下扩展:

// MARK: - Helpful methods
extension NSRange {
/// Convert to Range for given string
///
/// - Parameter string: the string
/// - Returns: range
func toRange(string: String) -> Range<String.Index> {
let range = string.index(string.startIndex, offsetBy: self.lowerBound)..<string.index(string.startIndex, offsetBy: self.upperBound)
return range
}
static func fromRange(_ range: Range<String.Index>, inString string: String) -> NSRange {
let s = string.distance(from: string.startIndex, to: range.lowerBound)
let e = string.distance(from: string.startIndex, to: range.upperBound)
return NSMakeRange(s, e-s)
}
}
// MARK: - Helpful methods
extension String {
/// Raplace string in phone
public func replacePhoneSubrange(_ range: NSRange, with string: String) -> String? {
if let phone = self.phone { // +11-111-111-1111 (111)
var numberString = phone.phoneNumber // 111111111111111
let newRange = self.toPhoneRange(range: range)
numberString.replaceSubrange(newRange.toRange(string: phone), with: string)
return numberString.phone
}
return nil
}
/// Phone number string
public var phoneNumber: String {
let components = self.components(
separatedBy: CharacterSet.decimalDigits.inverted)
var decimalString = NSString(string: components.joined(separator: ""))
while decimalString.hasPrefix("0") {
decimalString = decimalString.substring(from: 1) as NSString
}
return String(decimalString)
}
/// Get phone range
public func toPhoneRange(range: NSRange) -> NSRange {
let start = range.location
let end = start + range.length
let s2 = self.convertPhoneLocation(location: start)
let e2 = self.convertPhoneLocation(location: end)
return NSRange(location: s2, length: e2-s2)
}
/// Get cursor location for phone
public func convertPhoneLocation(location: Int) -> Int {
let substring = self[self.startIndex..<self.index(self.startIndex, offsetBy: location)]
return String(substring).phoneNumber.count
}
}
// MARK: - Helpful methods
extension UITextField {
/// Set cursor
///
/// - Parameter position: the position to set
func setCursor(_ position: Int) {
if let startPosition = self.position(from: self.beginningOfDocument, offset: position) {
let endPosition = startPosition
self.selectedTextRange = self.textRange(from: startPosition, to: endPosition)
}
}
}
// MARK: - Helpful methods
extension String {
/// phone formatting
public var phone: String? {
let components = self.components(
separatedBy: CharacterSet.decimalDigits.inverted)
var decimalString = NSString(string: components.joined(separator: ""))
while decimalString.hasPrefix("0") {
decimalString = decimalString.substring(from: 1) as NSString
}
let length = decimalString.length
let hasLeadingOne = length > 0 && length == 11
let hasLeadingTwo = length > 11
if length > 15 {
return nil
}
var index = 0 as Int
let formattedString = NSMutableString()
if hasLeadingOne || hasLeadingTwo {
let len = hasLeadingTwo ? 2 : 1
let areaCode = decimalString.substring(with: NSMakeRange(index, len))
formattedString.appendFormat("+%@-", areaCode)
index += len
}
if (length - index) > 3 {
let areaCode = decimalString.substring(with: NSMakeRange(index, 3))
formattedString.appendFormat("%@-", areaCode)
index += 3
}
if length - index == 4 && length == 7 { // xxx-xxxx
let prefix = decimalString.substring(with: NSMakeRange(index, 4))
formattedString.append(prefix)
index += 4
}
else if length - index > 3 {// xxx-xxx-x...
let prefix = decimalString.substring(with: NSMakeRange(index, 3))
formattedString.appendFormat("%@-", prefix)
index += 3
}
if length - index == 4 { // xxx-xxx-xxxx
let prefix = decimalString.substring(with: NSMakeRange(index, 4))
formattedString.append(prefix)
index += 4
}
// format phone extenstion
if length - index > 4 {
let prefix = decimalString.substring(with: NSMakeRange(index, 4))
formattedString.appendFormat("%@ ", prefix)
index += 4
}
let remainder = decimalString.substring(from: index)
if length > 12 {
formattedString.append("((remainder))")
}
else {
formattedString.append(remainder)
}
return (formattedString as String).trimmingCharacters(in: NSCharacterSet.whitespacesAndNewlines)
}
}

在文本字段委托方法中使用它:

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if range.length > 0 {
return true
}
if string == "" {
return false
}
if range.location > 11 {
return false
}
var originalText = textField.text
let replacementText = string.replacingOccurrences(of: " ", with: "")
if !CharacterSet.decimalDigits.isSuperset(of: CharacterSet(charactersIn: replacementText)) {
return false
}
if range.location == 3 || range.location == 7 {
originalText?.append("-")
textField.text = originalText
}
return true
}

最新更新