Last active
November 16, 2018 21:14
-
-
Save dedeexe/13589e0a69efd228da33705fdc1415cf to your computer and use it in GitHub Desktop.
Adding Mask to a TextField
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// UITextField+MaskPattern.swift | |
// MaskedTextField | |
// | |
// Created by dede.exe on 10/07/16. | |
// | |
// It's totally base on article : "http://vojtastavik.com/2015/03/29/real-time-formatting-in-uitextfield-swift-basics/" | |
// And I don't mind if the original author allow me to publish it :)... But I'll keep the reference | |
// I modified the original implementation to keep it as an Extension... | |
// | |
extension UITextField { | |
private struct FKFieldPatterns { | |
static var pattern = "pattern" | |
static var replacementChar = "replacementChar" | |
static var allowNumbers = "allowNumbers" | |
static var allowText = "allowText" | |
} | |
private var pattern : String { | |
if let result = objc_getAssociatedObject(self, &FKFieldPatterns.pattern) as? String { | |
return result | |
} | |
return "" | |
} | |
private var replacementChar : String { | |
if let result = objc_getAssociatedObject(self, &FKFieldPatterns.replacementChar) as? String { | |
return result | |
} | |
return "" | |
} | |
private var allowText : Bool { | |
if let result = objc_getAssociatedObject(self, &FKFieldPatterns.allowText) as? Bool { | |
return result | |
} | |
return true | |
} | |
private var allowNumbers : Bool { | |
if let result = objc_getAssociatedObject(self, &FKFieldPatterns.allowNumbers) as? Bool { | |
return result | |
} | |
return true | |
} | |
func formatPattern(pattern:String?=nil, replacementChar:String?=nil, allowText:Bool=true, allowNumbers:Bool=true) | |
{ | |
objc_setAssociatedObject(self, &FKFieldPatterns.pattern, pattern ?? "", .OBJC_ASSOCIATION_RETAIN) | |
objc_setAssociatedObject(self, &FKFieldPatterns.replacementChar, replacementChar ?? "*", .OBJC_ASSOCIATION_RETAIN) | |
objc_setAssociatedObject(self, &FKFieldPatterns.allowNumbers, allowNumbers, .OBJC_ASSOCIATION_RETAIN) | |
objc_setAssociatedObject(self, &FKFieldPatterns.allowText, allowText, .OBJC_ASSOCIATION_RETAIN) | |
registerNotifications() | |
} | |
private func registerNotifications() { | |
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(textDidChange), name: "UITextFieldTextDidChangeNotification", object: self) | |
} | |
private func removeNotifications() { | |
NSNotificationCenter.defaultCenter().removeObserver(self) | |
} | |
func prepareString(string:String) -> String | |
{ | |
var charSet : NSCharacterSet! | |
if allowText && allowNumbers { | |
charSet = NSCharacterSet.alphanumericCharacterSet().invertedSet | |
} | |
else if allowText { | |
charSet = NSCharacterSet.letterCharacterSet().invertedSet | |
} | |
else if allowNumbers { | |
charSet = NSCharacterSet.decimalDigitCharacterSet().invertedSet | |
} | |
let result = string.componentsSeparatedByCharactersInSet(charSet) | |
return result.joinWithSeparator("") | |
} | |
func textDidChange(notification : NSNotification) { | |
guard let text = self.text else { | |
return | |
} | |
if text.characters.count > 0 && pattern.characters.count > 0 | |
{ | |
var finalText = "" | |
var stop = false | |
let tempString = prepareString(text) | |
var formatIndex = pattern.startIndex | |
var tempIndex = tempString.startIndex | |
while !stop | |
{ | |
let formattingPatternRange = formatIndex ..< formatIndex.advancedBy(1) | |
if pattern.substringWithRange(formattingPatternRange) != String(replacementChar) { | |
finalText = finalText.stringByAppendingString(pattern.substringWithRange(formattingPatternRange)) | |
} | |
else if tempString.characters.count > 0 { | |
let pureStringRange = tempIndex ..< tempIndex.advancedBy(1) | |
finalText = finalText.stringByAppendingString(tempString.substringWithRange(pureStringRange)) | |
tempIndex = tempIndex.advancedBy(1) | |
} | |
formatIndex = formatIndex.advancedBy(1) | |
if formatIndex >= pattern.endIndex || tempIndex >= tempString.endIndex { | |
stop = true | |
} | |
self.text = finalText | |
} | |
} | |
} | |
} | |
//========================================================= | |
//= EXAMPLE = | |
//========================================================= | |
var creditCardTextField : UITextField! | |
var dateTextField : UITextField! | |
... | |
cardNumberTextField.formatPattern("**** **** **** ****", replacementChar:"*", allowText: false, allowNumbers: true) | |
dateTextfield.formatPattern("**/**/****", replacementChar:"*", allowText:false, allowNumbers: true) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
// You should move the "self.text = finalText" out of the loop. This way it will not call ui runloop to refresh the screen.
// Updated to Swift 4
// https://gist.github.com/dedeexe/13589e0a69efd228da33705fdc1415cf
// UITextField+MaskPattern.swift
// MaskedTextField
//
// Created by dede.exe on 10/07/16.
// Modified by João Paulo Serodio on 11/16/18.
//
// It's totally base on article : "http://vojtastavik.com/2015/03/29/real-time-formatting-in-uitextfield-swift-basics/"
// And I don't mind if the original author allow me to publish it :)... But I'll keep the reference
// I modified the original implementation to keep it as an Extension...
//
extension UITextField {
}