Add InputMask.xcodeproj into your project/workspace;
Go to your target’s settings, add InputMask.framework under the Embedded Binaries section
For ObjC projects:
(~Xcode 8.x) make sure Build Options has Embedded Content Contains Swift Code enabled;
import bridging header.
📢 Communication, Questions & Issues
Please take a closer look at our Known issues section before you incorporate our library into your project.
For your bugreports and feature requests please file new issues via GitHub.
Should you have any questions, please search for closed issues or ask questions at StackOverflow with the input-mask tag.
❗Known issues
UITextFieldTextDidChange notification and target-action editingChanged event
UITextField with assigned MaskedTextFieldDelegate object won’t issue UITextFieldTextDidChange notifications and editingChanged control events. This happens due to the textField(_:shouldChangeCharactersIn:replacementString:) method implementation, which always returns false.
Consider using following workaround in case if you do really need to catch editing events:
Please, avoid at all costs sending SDK events and notifications manually.
Carthage vs. IBDesignables, IBInspectables, views and their outlets
Interface Builder struggles to support modules imported in a form of a dynamic framework. For instance, custom views annotated as IBDesignable, containing IBInspectable and IBOutlet fields aren’t recognized properly from the drag’n’dropped *.framework.
In case you are using our library as a Carthage-built dynamic framework, be aware you won’t be able to easily wire your MaskedTextFieldDelegate objects and their listeners from storyboards in your project. There is a couple of workarounds described in the corresponding discussion, though.
Also, consider filing a radar to Apple, like this one.
Cut action doesn’t put text into the pasteboard
When you cut text, characters get deleted yet you won’t be able to paste them somewhere as they aren’t actually in your pasteboard.
iOS hardwires UIMenuController‘s cut action to the UITextFieldDelegate‘s textField(_:shouldChangeCharactersIn:replacementString:) return value. This means “Cut” behaviour actually depends on the ability to edit the text.
Bad news are, our library returns false in textField(_:shouldChangeCharactersIn:replacementString:), and heavily depends on this false. It would require us to rewrite a lot of logic in order to change this design, and there’s no guarantee we’ll be able to do so.
Essentially, there’s no distinct way to differentiate “Cut selection” and “Delete selection” actions on the UITextFieldDelegate side. However, you may consider using a workaround, which will require you to subclass UITextField overriding its cut(sender:) method like this:
From our library perspective, this looks like a highly invasive solution. Thus, in the long term, we are going to investigate a “costly” method to bring the behaviour matching the iOS SDK logic. Yet, here “long term” might mean months.
Incorrect cursor position after pasting
Shortly after new text is being pasted from the clipboard, every UITextInput receives a new value for its selectedTextRange property from the system. This new range is not consistent with the formatted text and calculated caret position most of the time, yet it’s being assigned just after set caretPosition call.
To ensure correct caret position is set, it might be assigned asynchronously (presumably after a vanishingly small delay), if caret movement is set to be non-atomic; see MaskedTextFieldDelegate.atomicCursorMovement property.
MaskedTextInputListener
In case you are wondering why do we have two separate UITextFieldDelegate and UITextViewDelegate implementations, the answer is simple: prior to iOS 11UITextField and UITextView had different behaviour in some key situations, which made it difficult to implement common logic.
Both had the same bug with the UITextInput.beginningOfDocument property, which rendered impossible to use the generic UITextInput protocol UITextField and UITextView have in common.
Since iOS 11 most of the things received their fixes (except for the UITextViewedge case). In case your project is not going to support anything below 11, consider using the modern MaskedTextInputListener.
Input masks restrict data input and allow you to guide users to enter correct values.
Check out our wiki for quick start and further reading.
⚙️ Features
0123456
from+1 (999) 012-34-56
)+1 201 456-7890
)💳 Examples
+1 ([000]) [000] [00] [00]
[00]{.}[00]{.}[9900]
[AA]-[00000099]
[099]{.}[099]{.}[099]{.}[099]
[0000] [0000] [0000] [0000]
GB[00] [____] [0000] [0000] [0000] [00]
🛠️ Installation
Swift Package Manager
CocoaPods
Manual
git clone
this repository;InputMask.xcodeproj
into your project/workspace;InputMask.framework
under theEmbedded Binaries
sectionObjC
projects:Build Options
hasEmbedded Content Contains Swift Code
enabled;📢 Communication, Questions & Issues
Please take a closer look at our Known issues section before you incorporate our library into your project.
For your bugreports and feature requests please file new issues via GitHub.
Should you have any questions, please search for closed issues or ask questions at StackOverflow with the
input-mask
tag.❗Known issues
UITextFieldTextDidChange
notification and target-actioneditingChanged
eventUITextField
with assignedMaskedTextFieldDelegate
object won’t issueUITextFieldTextDidChange
notifications andeditingChanged
control events. This happens due to thetextField(_:shouldChangeCharactersIn:replacementString:)
method implementation, which always returnsfalse
.Consider using following workaround in case if you do really need to catch editing events:
Please, avoid at all costs sending SDK events and notifications manually.
Carthage vs. IBDesignables, IBInspectables, views and their outlets
Interface Builder struggles to support modules imported in a form of a dynamic framework. For instance, custom views annotated as IBDesignable, containing IBInspectable and IBOutlet fields aren’t recognized properly from the drag’n’dropped *.framework.
In case you are using our library as a Carthage-built dynamic framework, be aware you won’t be able to easily wire your
MaskedTextFieldDelegate
objects and their listeners from storyboards in your project. There is a couple of workarounds described in the corresponding discussion, though.Also, consider filing a radar to Apple, like this one.
Cut action doesn’t put text into the pasteboard
When you cut text, characters get deleted yet you won’t be able to paste them somewhere as they aren’t actually in your pasteboard.
iOS hardwires
UIMenuController
‘s cut action to theUITextFieldDelegate
‘stextField(_:shouldChangeCharactersIn:replacementString:)
return value. This means “Cut” behaviour actually depends on the ability to edit the text.Bad news are, our library returns
false
intextField(_:shouldChangeCharactersIn:replacementString:)
, and heavily depends on thisfalse
. It would require us to rewrite a lot of logic in order to change this design, and there’s no guarantee we’ll be able to do so.Essentially, there’s no distinct way to differentiate “Cut selection” and “Delete selection” actions on the
UITextFieldDelegate
side. However, you may consider using a workaround, which will require you to subclassUITextField
overriding itscut(sender:)
method like this:From our library perspective, this looks like a highly invasive solution. Thus, in the long term, we are going to investigate a “costly” method to bring the behaviour matching the iOS SDK logic. Yet, here “long term” might mean months.
Incorrect cursor position after pasting
Shortly after new text is being pasted from the clipboard, every
UITextInput
receives a new value for itsselectedTextRange
property from the system. This new range is not consistent with the formatted text and calculated caret position most of the time, yet it’s being assigned just afterset caretPosition
call.To ensure correct caret position is set, it might be assigned asynchronously (presumably after a vanishingly small delay), if caret movement is set to be non-atomic; see
MaskedTextFieldDelegate.atomicCursorMovement
property.MaskedTextInputListener
In case you are wondering why do we have two separate
UITextFieldDelegate
andUITextViewDelegate
implementations, the answer is simple: prior to iOS 11UITextField
andUITextView
had different behaviour in some key situations, which made it difficult to implement common logic.Both had the same bug with the
UITextInput.beginningOfDocument
property, which rendered impossible to use the genericUITextInput
protocolUITextField
andUITextView
have in common.Since iOS 11 most of the things received their fixes (except for the
UITextView
edge case). In case your project is not going to support anything below 11, consider using the modernMaskedTextInputListener
.🙏 Special thanks
These folks rock:
♻️ License
The library is distributed under the MIT LICENSE.