How can I set the maximum amount of characters in a UITextField on the iPhone SDK when I load up a UIView?
While the UITextField class has no max length property, it's relatively simple to get this functionality by setting the text field's delegate and implementing the following delegate method:
Objective-C
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
// Prevent crashing undo bug – see note below.
if(range.length + range.location > textField.text.length)
{
return NO;
}
NSUInteger newLength = [textField.text length] + [string length] - range.length;
return newLength <= 25;
}
Swift
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let currentCharacterCount = textField.text?.count ?? 0
if range.length + range.location > currentCharacterCount {
return false
}
let newLength = currentCharacterCount + string.count - range.length
return newLength <= 25
}
Before the text field changes, the UITextField asks the delegate if the specified text should be changed. The text field has not changed at this point, so we grab it's current length and the string length we're inserting (either through pasting copied text or typing a single character using the keyboard), minus the range length. If this value is too long (more than 25 characters in this example), return NO to prohibit the change.
When typing in a single character at the end of a text field, the range.location will be the current field's length, and range.length will be 0 because we're not replacing/deleting anything. Inserting into the middle of a text field just means a different range.location, and pasting multiple characters just means string has more than one character in it.
Deleting single characters or cutting multiple characters is specified by a range with a non-zero length, and an empty string. Replacement is just a range deletion with a non-empty string.
A note on the crashing "undo" bug
As is mentioned in the comments, there is a bug with UITextField that can lead to a crash.
If you paste in to the field, but the paste is prevented by your validation implementation, the paste operation is still recorded in the application's undo buffer. If you then fire an undo (by shaking the device and confirming an Undo), the UITextField will attempt to replace the string it thinks it pasted in to itself with an empty string. This will crash because it never actually pasted the string in to itself. It will try to replace a part of the string that doesn't exist.
Fortunately you can protect the UITextField from killing itself like this. You just need to ensure that the range it proposes to replace does exist within its current string. This is what the initial sanity check above does.
swift 3.0 with copy and paste working fine.
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
let str = (textView.text + text)
if str.characters.count <= 10 {
return true
}
textView.text = str.substring(to: str.index(str.startIndex, offsetBy: 10))
return false
}
Hope it's helpful to you.
Swift 4
import UIKit
private var kAssociationKeyMaxLength: Int = 0
extension UITextField {
#IBInspectable var maxLength: Int {
get {
if let length = objc_getAssociatedObject(self, &kAssociationKeyMaxLength) as? Int {
return length
} else {
return Int.max
}
}
set {
objc_setAssociatedObject(self, &kAssociationKeyMaxLength, newValue, .OBJC_ASSOCIATION_RETAIN)
addTarget(self, action: #selector(checkMaxLength), for: .editingChanged)
}
}
#objc func checkMaxLength(textField: UITextField) {
guard let prospectiveText = self.text,
prospectiveText.count > maxLength
else {
return
}
let selection = selectedTextRange
let indexEndOfText = prospectiveText.index(prospectiveText.startIndex, offsetBy: maxLength)
let substring = prospectiveText[..<indexEndOfText]
text = String(substring)
selectedTextRange = selection
}
}
Edit: memory leak issue fixed.
Thank you august! (Post)
This is the code that I ended up with which works:
#define MAX_LENGTH 20
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if (textField.text.length >= MAX_LENGTH && range.length == 0)
{
return NO; // return NO to not change text
}
else
{return YES;}
}
To complete August answer, a possible implementation of the proposed function (see UITextField's delegate).
I did not test domness code but mine do not get stuck if the user reached the limit and it is compatible with a new string that comes replacing a smaller or equal one.
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
//limit the size :
int limit = 20;
return !([textField.text length]>limit && [string length] > range.length);
}
You can't do this directly - UITextField has no maxLength attribute, but you can set the UITextField's delegate, then use:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
Often you have multiple input fields with a different length.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
int allowedLength;
switch(textField.tag) {
case 1:
allowedLength = MAXLENGTHNAME; // triggered for input fields with tag = 1
break;
case 2:
allowedLength = MAXLENGTHADDRESS; // triggered for input fields with tag = 2
break;
default:
allowedLength = MAXLENGTHDEFAULT; // length default when no tag (=0) value =255
break;
}
if (textField.text.length >= allowedLength && range.length == 0) {
return NO; // Change not allowed
} else {
return YES; // Change allowed
}
}
The best way would be to set up a notification on the text changing. In your -awakeFromNib of your view controller method you'll want:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(limitTextField:) name:#"UITextFieldTextDidChangeNotification" object:myTextField];
Then in the same class add:
- (void)limitTextField:(NSNotification *)note {
int limit = 20;
if ([[myTextField stringValue] length] > limit) {
[myTextField setStringValue:[[myTextField stringValue] substringToIndex:limit]];
}
}
Then link up the outlet myTextField to your UITextField and it will not let you add any more characters after you hit the limit. Be sure to add this to your dealloc method:
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"UITextFieldTextDidChangeNotification" object:myTextField];
I created this UITextFieldLimit subclass:
Multiple textfields supported
Set the text length limit
Paste prevention
Displays a label of left characters inside the textfield, get hidden when you stop editing.
Shake animation when no characters left.
Grab the UITextFieldLimit.h and UITextFieldLimit.m from this GitHub repository:
https://github.com/JonathanGurebo/UITextFieldLimit
and begin to test!
Mark your storyboard-created UITextField and link it to my subclass using the Identity Inspector:
Then you can link it to an IBOutlet and set the limit(default is 10).
Your ViewController.h file should contain: (if you wan't to modify the setting, like the limit)
#import "UITextFieldLimit.h"
/.../
#property (weak, nonatomic) IBOutlet UITextFieldLimit *textFieldLimit; // <--Your IBOutlet
Your ViewController.m file should #synthesize textFieldLimit.
Set the text length limit in your ViewController.m file:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[textFieldLimit setLimit:25];// <-- and you won't be able to put more than 25 characters in the TextField.
}
Hope the class helps you. Good luck!
This should be enough to solve the problem (replace 4 by the limit u want). Just make sure to add delegate in IB.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSString *newString = [textField.text stringByReplacingCharactersInRange:range withString:string];
return (newString.length<=4);
}
Use below extension to set the maximum character length of a UITextField and UITextView.
Swift 4.0
private var kAssociationKeyMaxLength: Int = 0
private var kAssociationKeyMaxLengthTextView: Int = 0
extension UITextField {
#IBInspectable var maxLength: Int {
get {
if let length = objc_getAssociatedObject(self, &kAssociationKeyMaxLength) as? Int {
return length
} else {
return Int.max
}
}
set {
objc_setAssociatedObject(self, &kAssociationKeyMaxLength, newValue, .OBJC_ASSOCIATION_RETAIN)
addTarget(self, action: #selector(checkMaxLength), for: .editingChanged)
}
}
#objc func checkMaxLength(textField: UITextField) {
guard let prospectiveText = self.text,
prospectiveText.count > maxLength
else {
return
}
let selection = selectedTextRange
let indexEndOfText = prospectiveText.index(prospectiveText.startIndex, offsetBy: maxLength)
let substring = prospectiveText[..<indexEndOfText]
text = String(substring)
selectedTextRange = selection
}
}
UITextView
extension UITextView:UITextViewDelegate {
#IBInspectable var maxLength: Int {
get {
if let length = objc_getAssociatedObject(self, &kAssociationKeyMaxLengthTextView) as? Int {
return length
} else {
return Int.max
}
}
set {
self.delegate = self
objc_setAssociatedObject(self, &kAssociationKeyMaxLengthTextView, newValue, .OBJC_ASSOCIATION_RETAIN)
}
}
public func textViewDidChange(_ textView: UITextView) {
checkMaxLength(textField: self)
}
#objc func checkMaxLength(textField: UITextView) {
guard let prospectiveText = self.text,
prospectiveText.count > maxLength
else {
return
}
let selection = selectedTextRange
let indexEndOfText = prospectiveText.index(prospectiveText.startIndex, offsetBy: maxLength)
let substring = prospectiveText[..<indexEndOfText]
text = String(substring)
selectedTextRange = selection
}
}
You can set limit below.
I simulate the actual string replacement that's about to happen to calculate that future string's length:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSString *newString = [textField.text stringByReplacingCharactersInRange:range withString:string];
if([newString length] > maxLength)
return NO;
return YES;
}
There is generic solution for setting max length in Swift.
By IBInspectable you can add new Attribute in Xcode Attribute Inspector.
import UIKit
private var maxLengths = [UITextField: Int]()
extension UITextField {
#IBInspectable var maxLength: Int {
get {
guard let length = maxLengths[self]
else {
return Int.max
}
return length
}
set {
maxLengths[self] = newValue
addTarget(
self,
action: Selector("limitLength:"),
forControlEvents: UIControlEvents.EditingChanged
)
}
}
func limitLength(textField: UITextField) {
guard let prospectiveText = textField.text
where prospectiveText.characters.count > maxLength else {
return
}
let selection = selectedTextRange
text = prospectiveText.substringWithRange(
Range<String.Index>(prospectiveText.startIndex ..< prospectiveText.startIndex.advancedBy(maxLength))
)
selectedTextRange = selection
}
}
Swift 3 version
//***** This will NOT work with Swift 2.x! *****//
First create a new Swift file : TextFieldMaxLength.swift,
and then add the code below:
import UIKit
private var maxLengths = [UITextField: Int]()
extension UITextField {
#IBInspectable var maxLength: Int {
get {
guard let length = maxLengths[self]
else {
return Int.max
}
return length
}
set {
maxLengths[self] = newValue
addTarget(
self,
action: #selector(limitLength),
for: UIControlEvents.editingChanged
)
}
}
func limitLength(textField: UITextField) {
guard let prospectiveText = textField.text,
prospectiveText.characters.count > maxLength
else {
return
}
let selection = selectedTextRange
let maxCharIndex = prospectiveText.index(prospectiveText.startIndex, offsetBy: maxLength)
text = prospectiveText.substring(to: maxCharIndex)
selectedTextRange = selection
}
}
and then you will see in Storyboard a new field (Max Length) when you select any TextField
if you still have more questions check out this link: http://www.globalnerdy.com/2016/05/18/ios-programming-trick-how-to-use-xcode-to-set-a-text-fields-maximum-length-visual-studio-style/
Using Interface builder you can link and get the event for "Editing changed" in any of your function.
Now there you can put check for the length
- (IBAction)onValueChange:(id)sender
{
NSString *text = nil;
int MAX_LENGTH = 20;
switch ([sender tag] )
{
case 1:
{
text = myEditField.text;
if (MAX_LENGTH < [text length]) {
myEditField.text = [text substringToIndex:MAX_LENGTH];
}
}
break;
default:
break;
}
}
The following code is similar to sickp's answer but handles correctly copy-paste operations. If you try to paste a text that is longer than the limit, the following code will truncate the text to fit the limit instead of refusing the paste operation completely.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
static const NSUInteger limit = 70; // we limit to 70 characters
NSUInteger allowedLength = limit - [textField.text length] + range.length;
if (string.length > allowedLength) {
if (string.length > 1) {
// get at least the part of the new string that fits
NSString *limitedString = [string substringToIndex:allowedLength];
NSMutableString *newString = [textField.text mutableCopy];
[newString replaceCharactersInRange:range withString:limitedString];
textField.text = newString;
}
return NO;
} else {
return YES;
}
}
To make it work with cut & paste of strings of any length, I would suggest changing the function to something like:
#define MAX_LENGTH 20
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSInteger insertDelta = string.length - range.length;
if (textField.text.length + insertDelta > MAX_LENGTH)
{
return NO; // the new string would be longer than MAX_LENGTH
}
else {
return YES;
}
}
Swift 4
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
guard let text = textField.text else { return true }
let newLength = text.count + string.count - range.length
return newLength <= 10
}
Swift 2.0 +
First of all create a class for this process. Lets call it StringValidator.swift.
Then just paste the following code inside it.
import Foundation
extension String {
func containsCharactersIn(matchCharacters: String) -> Bool {
let characterSet = NSCharacterSet(charactersInString: matchCharacters)
return self.rangeOfCharacterFromSet(characterSet) != nil
}
func containsOnlyCharactersIn(matchCharacters: String) -> Bool {
let disallowedCharacterSet = NSCharacterSet(charactersInString: matchCharacters).invertedSet
return self.rangeOfCharacterFromSet(disallowedCharacterSet) == nil
}
func doesNotContainCharactersIn(matchCharacters: String) -> Bool {
let characterSet = NSCharacterSet(charactersInString: matchCharacters)
return self.rangeOfCharacterFromSet(characterSet) == nil
}
func isNumeric() -> Bool
{
let scanner = NSScanner(string: self)
scanner.locale = NSLocale.currentLocale()
return scanner.scanDecimal(nil) && scanner.atEnd
}
}
Now save the class.....
Usage..
Now goto your viewController.swift class and make your textfield's outlets as..
#IBOutlet weak var contactEntryTxtFld: UITextField! //First textfield
#IBOutlet weak var contactEntryTxtFld2: UITextField! //Second textfield
Now goto the textfield's shouldChangeCharactersInRange method and use like the following.
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
if string.characters.count == 0 {
return true
}
let latestText = textField.text ?? ""
let checkAbleText = (latestText as NSString).stringByReplacingCharactersInRange(range, withString: string)
switch textField {
case contactEntryTxtFld:
return checkAbleText.containsOnlyCharactersIn("0123456789") && prospectiveText.characters.count <= 5
case contactEntryTxtFld2:
return checkAbleText.containsOnlyCharactersIn("0123456789") && prospectiveText.characters.count <= 5
default:
return true
}
}
Don't forget to set the delegate protocol/methods of textfields.
Let me explain about this... I am using the simple extension process of string which I wrote inside an another class. Now I am just calling those extension methods from another class where I need them by adding check and maximum value.
Features...
It will set maximum limit of a particular textfield.
It will set type of accepted keys for particular textfield.
Types...
containsOnlyCharactersIn //Accepts only Characters.
containsCharactersIn //Accepts combination of characters
doesNotContainsCharactersIn //Will not accept characters
Hope this helped....
Thanks..
swift 3.0
This code is working fine when you are paste string more than your character limits.
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
let str = (textView.text + text)
if str.characters.count <= 10 {
return true
}
textView.text = str.substring(to: str.index(str.startIndex, offsetBy: 10))
return false
}
Thanks for your votes. :)
I give a supplementary answer based on #Frouo. I think his answer is the most beautiful way. Becuase it's a common control we can reuse.
private var kAssociationKeyMaxLength: Int = 0
extension UITextField {
#IBInspectable var maxLength: Int {
get {
if let length = objc_getAssociatedObject(self, &kAssociationKeyMaxLength) as? Int {
return length
} else {
return Int.max
}
}
set {
objc_setAssociatedObject(self, &kAssociationKeyMaxLength, newValue, .OBJC_ASSOCIATION_RETAIN)
self.addTarget(self, action: #selector(checkMaxLength), for: .editingChanged)
}
}
func checkMaxLength(textField: UITextField) {
guard !self.isInputMethod(), let prospectiveText = self.text,
prospectiveText.count > maxLength
else {
return
}
let selection = selectedTextRange
let maxCharIndex = prospectiveText.index(prospectiveText.startIndex, offsetBy: maxLength)
text = prospectiveText.substring(to: maxCharIndex)
selectedTextRange = selection
}
//The method is used to cancel the check when use Chinese Pinyin input method.
//Becuase the alphabet also appears in the textfield when inputting, we should cancel the check.
func isInputMethod() -> Bool {
if let positionRange = self.markedTextRange {
if let _ = self.position(from: positionRange.start, offset: 0) {
return true
}
}
return false
}
}
This is the correct way to handle max length on UITextField, it allows the return key to exit the resign the textfield as first responder and lets the user backspace when they reach the limit
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
int MAX_LENGHT = 5;
if([string isEqualToString:#"\n"])
{
[textField resignFirstResponder];
return FALSE;
}
else if(textField.text.length > MAX_LENGHT-1)
{
if([string isEqualToString:#""] && range.length == 1)
{
return TRUE;
}
else
{
return FALSE;
}
}
else
{
return TRUE;
}
}
What about this simple approach. Its working fine for me.
extension UITextField {
func charactersLimit(to:Int) {
if (self.text!.count > to) {
self.deleteBackward()
}
}
}
Then:
someTextField.charactersLimit(to:16)
Other answers do not handle the case where user can paste a long string from clipboard. If I paste a long string it should just be truncated but shown.
Use this in your delegate:
static const NSUInteger maxNoOfCharacters = 5;
-(IBAction)textdidChange:(UITextField * )textField
{
NSString * text = textField.text;
if(text.length > maxNoOfCharacters)
{
text = [text substringWithRange:NSMakeRange(0, maxNoOfCharacters)];
textField.text = text;
}
// use 'text'
}
Got it down to 1 line of code :)
Set your text view's delegate to "self" then add the <UITextViewDelegate> in your .h and the following code in your .m .... you can adjust the number "7" to be whatever you want your MAXIMUM number of characters to be.
-(BOOL)textView:(UITextView *)a shouldChangeTextInRange:(NSRange)b replacementText:(NSString *)c {
return ((a.text.length+c.length<=7)+(c.length<1)+(b.length>=c.length)>0);
}
This code accounts for typing new characters, deleting characters, selecting characters then typing or deleting, selecting characters and cutting, pasting in general, and selecting characters and pasting.
Done!
Alternatively, another cool way to write this code with bit-operations would be
-(BOOL)textView:(UITextView *)a shouldChangeTextInRange:(NSRange)b replacementText:(NSString *)c {
return 0^((a.text.length+c.length<=7)+(c.length<1)+(b.length>=c.length));
}
I have open sourced a UITextField subclass, STATextField, that offers this functionality (and much more) with its maxCharacterLength property.
now how many characters u want just give values
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSUInteger newLength = [textField.text length] + [string length] - range.length;
return (newLength > 25) ? NO : YES;
}
Use this code here RESTRICTED_LENGTH is length you want to restrict for textfield.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if (textField == nameTF) {
int limit = RESTRICTED_LENGTH - 1;
return !([textField.text length]>limit && [string length] > range.length);
}
else
{
return YES;
}
return NO;
}
I did this in Swift for an 8 character limit when using a number pad.
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
return !(textField.text?.characters.count == MAX_LENGTH && string != "")
}
I had to test for string != "" to allow the delete button to work on the number pad, otherwise it wouldn't allow deleting characters in the text field after it reached its max.
For Xamarin:
YourTextField.ShouldChangeCharacters =
delegate(UITextField textField, NSRange range, string replacementString)
{
return (range.Location + replacementString.Length) <= 4; // MaxLength == 4
};
I have implemented a UITextField Extension to add a maxLength property to it.
It's based on Xcode 6 IBInspectables, so you can set the maxLength limit on the Interface builder.
Here is the implementation:
UITextField+MaxLength.h
#import <UIKit/UIKit.h>
#interface UITextField_MaxLength : UITextField<UITextFieldDelegate>
#property (nonatomic)IBInspectable int textMaxLength;
#end
UITextField+MaxLength.m
#import "UITextField+MaxLength.h"
#interface UITextField_MaxLength()
#property (nonatomic, assign) id <UITextFieldDelegate> superDelegate;
#end
#implementation UITextField_MaxLength
- (BOOL)textField:(UITextField *) textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
//validate the length, only if it's set to a non zero value
if (self.textMaxLength>0) {
if(range.length + range.location > textField.text.length)
return NO;
if (textField.text.length+string.length - range.length>self.textMaxLength) {
return NO;
}
}
//if length validation was passed, query the super class to see if the delegate method is implemented there
if (self.superDelegate && [self.superDelegate respondsToSelector:#selector(textField:shouldChangeCharactersInRange:replacementString:)]) {
return [self.superDelegate textField:textField shouldChangeCharactersInRange:range replacementString:string];
}
else{
//if the super class does not implement the delegate method, simply return YES as the length validation was passed
return YES;
}
}
- (void)setDelegate:(id<UITextFieldDelegate>)delegate {
if (delegate == self)
return;
self.superDelegate = delegate;
[super setDelegate:self];
}
//forward all non overriden delegate methods
- (id)forwardingTargetForSelector:(SEL)aSelector {
if ([self.superDelegate respondsToSelector:aSelector])
return self.superDelegate;
return [super forwardingTargetForSelector:aSelector];
}
- (BOOL)respondsToSelector:(SEL)aSelector {
if ([self.superDelegate respondsToSelector:aSelector])
return YES;
return [super respondsToSelector:aSelector];
}
#end
Related
I just want to know how to limit uitextfield range, i.e I have one textbox in that I enter values 10 digit. If I try to type more than 10 digit my textfield should not accept the values. To be very simple I want only 10 digit should be enter in the textfield.
I work out this code but its not worked for me:
- (BOOL)textField:(UITextField *) textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSUInteger oldLength = [textField.text length];
NSUInteger replacementLength = [string length];
NSUInteger rangeLength = range.length;
NSUInteger newLength = oldLength - rangeLength + replacementLength;
BOOL returnKey = [string rangeOfString: #"\n"].location != NSNotFound;
return newLength <= MAXLENGTH || returnKey;
}
To limit a text input's length implement this method of UITextFieldDelegate and check a text's length after changing:
- (BOOL) textField:(UITextField *)textField
shouldChangeCharactersInRange:(NSRange)range
replacementString:(NSString *)string {
NSString *resultText = [textField.text stringByReplacingCharactersInRange:range
withString:string];
return resultText.length <= 10;
}
In Swift 3.0
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let charsLimit = 10
let startingLength = textField.text?.characters.count ?? 0
let lengthToAdd = string.characters.count
let lengthToReplace = range.length
let newLength = startingLength + lengthToAdd - lengthToReplace
return newLength <= charsLimit
}
Try below code that is restricted to 10 digital text.
- (BOOL)textField:(UITextField *) textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSInteger length = [textField.text length];
if (length>9 && ![string isEqualToString:#""]) {
return NO;
}
// This code will provide protection if user copy and paste more then 10 digit text
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if ([textField.text length]>10) {
textField.text = [textField.text substringToIndex:10];
}
});
return YES;
}
Hope this help you.
Swift 3 Version
func textField(_ textField: UITextField, shouldChangeCharactersIn range:NSRange, replacementString string: String) -> Bool
{
let currentCharacterCount = textField.text?.characters.count ?? 0
if (range.length + range.location > currentCharacterCount){
return false
}
let newLength = currentCharacterCount + string.characters.count - range.length
return newLength <= 10
}
Try this.
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{
if(range.length + range.location > textField.text.length)
{
return NO;
}
NSUInteger newLength = [textField.text length] + [string length] - range.length;
return (newLength > 10) ? NO : YES;}
You can use this...i hope it will help you/
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
int lenght1 = code.text.length - range.length + string.length;
if (textField.text.length >= 4 && range.length == 0)
return NO;
I built a really nice subclass of UITextField to limit the number of characters inputted into a field. Here you go!:
public class NumberFormattedTextField : UITextField {
#IBInspectable public var maximumCharacters = 10 {
didSet {
format()
}
}
public override func awakeFromNib() {
format()
addTarget(self, action: "format", forControlEvents: .EditingChanged)
}
func format() {
let len = text.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)
// truncate for max characters if needed
if len > maximumCharacters {
text = text[1...maximumCharacters] // pulls in the last entered character and drops the first one off to preserve length
}
}
}
This depends on a subscript for String. Here's that too:
public extension String {
public subscript (r: Range<Int>) -> String? {
let l = self.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)
if r.startIndex <= l && r.endIndex <= l {
return substringWithRange(Range(start: advance(startIndex, r.startIndex), end: advance(startIndex, r.endIndex)))
} else {
return nil
}
}
}
I have build a subclass based on the answer given by Oxcug without the need for creating an extension in String file and max characters length can be set from storyboard and from swift file as well.:
#IBDesignable class CustomTextField: UITextField {
#IBInspectable var maximumCharacters: Int = 80 {
didSet {
limitCharacters()
}
}
override func awakeFromNib() {
super.awakeFromNib()
limitCharacters()
addTarget(self, action: #selector(CustomTextField.limitCharacters), for: .editingChanged)
}
func limitCharacters() {
guard text != nil else {
return
}
if (text?.characters.count)! > maximumCharacters {
if let range = text?.index(before: (text?.endIndex)!) {
text = text?.substring(to: range)
}
}
}
}
Best Solution:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
return range.location < 10 //Here 10 is your character limit
}
How can I set the maximum amount of characters in a UITextField on the iPhone SDK when I load up a UIView?
While the UITextField class has no max length property, it's relatively simple to get this functionality by setting the text field's delegate and implementing the following delegate method:
Objective-C
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
// Prevent crashing undo bug – see note below.
if(range.length + range.location > textField.text.length)
{
return NO;
}
NSUInteger newLength = [textField.text length] + [string length] - range.length;
return newLength <= 25;
}
Swift
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let currentCharacterCount = textField.text?.count ?? 0
if range.length + range.location > currentCharacterCount {
return false
}
let newLength = currentCharacterCount + string.count - range.length
return newLength <= 25
}
Before the text field changes, the UITextField asks the delegate if the specified text should be changed. The text field has not changed at this point, so we grab it's current length and the string length we're inserting (either through pasting copied text or typing a single character using the keyboard), minus the range length. If this value is too long (more than 25 characters in this example), return NO to prohibit the change.
When typing in a single character at the end of a text field, the range.location will be the current field's length, and range.length will be 0 because we're not replacing/deleting anything. Inserting into the middle of a text field just means a different range.location, and pasting multiple characters just means string has more than one character in it.
Deleting single characters or cutting multiple characters is specified by a range with a non-zero length, and an empty string. Replacement is just a range deletion with a non-empty string.
A note on the crashing "undo" bug
As is mentioned in the comments, there is a bug with UITextField that can lead to a crash.
If you paste in to the field, but the paste is prevented by your validation implementation, the paste operation is still recorded in the application's undo buffer. If you then fire an undo (by shaking the device and confirming an Undo), the UITextField will attempt to replace the string it thinks it pasted in to itself with an empty string. This will crash because it never actually pasted the string in to itself. It will try to replace a part of the string that doesn't exist.
Fortunately you can protect the UITextField from killing itself like this. You just need to ensure that the range it proposes to replace does exist within its current string. This is what the initial sanity check above does.
swift 3.0 with copy and paste working fine.
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
let str = (textView.text + text)
if str.characters.count <= 10 {
return true
}
textView.text = str.substring(to: str.index(str.startIndex, offsetBy: 10))
return false
}
Hope it's helpful to you.
Swift 4
import UIKit
private var kAssociationKeyMaxLength: Int = 0
extension UITextField {
#IBInspectable var maxLength: Int {
get {
if let length = objc_getAssociatedObject(self, &kAssociationKeyMaxLength) as? Int {
return length
} else {
return Int.max
}
}
set {
objc_setAssociatedObject(self, &kAssociationKeyMaxLength, newValue, .OBJC_ASSOCIATION_RETAIN)
addTarget(self, action: #selector(checkMaxLength), for: .editingChanged)
}
}
#objc func checkMaxLength(textField: UITextField) {
guard let prospectiveText = self.text,
prospectiveText.count > maxLength
else {
return
}
let selection = selectedTextRange
let indexEndOfText = prospectiveText.index(prospectiveText.startIndex, offsetBy: maxLength)
let substring = prospectiveText[..<indexEndOfText]
text = String(substring)
selectedTextRange = selection
}
}
Edit: memory leak issue fixed.
Thank you august! (Post)
This is the code that I ended up with which works:
#define MAX_LENGTH 20
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if (textField.text.length >= MAX_LENGTH && range.length == 0)
{
return NO; // return NO to not change text
}
else
{return YES;}
}
To complete August answer, a possible implementation of the proposed function (see UITextField's delegate).
I did not test domness code but mine do not get stuck if the user reached the limit and it is compatible with a new string that comes replacing a smaller or equal one.
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
//limit the size :
int limit = 20;
return !([textField.text length]>limit && [string length] > range.length);
}
You can't do this directly - UITextField has no maxLength attribute, but you can set the UITextField's delegate, then use:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
Often you have multiple input fields with a different length.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
int allowedLength;
switch(textField.tag) {
case 1:
allowedLength = MAXLENGTHNAME; // triggered for input fields with tag = 1
break;
case 2:
allowedLength = MAXLENGTHADDRESS; // triggered for input fields with tag = 2
break;
default:
allowedLength = MAXLENGTHDEFAULT; // length default when no tag (=0) value =255
break;
}
if (textField.text.length >= allowedLength && range.length == 0) {
return NO; // Change not allowed
} else {
return YES; // Change allowed
}
}
The best way would be to set up a notification on the text changing. In your -awakeFromNib of your view controller method you'll want:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(limitTextField:) name:#"UITextFieldTextDidChangeNotification" object:myTextField];
Then in the same class add:
- (void)limitTextField:(NSNotification *)note {
int limit = 20;
if ([[myTextField stringValue] length] > limit) {
[myTextField setStringValue:[[myTextField stringValue] substringToIndex:limit]];
}
}
Then link up the outlet myTextField to your UITextField and it will not let you add any more characters after you hit the limit. Be sure to add this to your dealloc method:
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"UITextFieldTextDidChangeNotification" object:myTextField];
I created this UITextFieldLimit subclass:
Multiple textfields supported
Set the text length limit
Paste prevention
Displays a label of left characters inside the textfield, get hidden when you stop editing.
Shake animation when no characters left.
Grab the UITextFieldLimit.h and UITextFieldLimit.m from this GitHub repository:
https://github.com/JonathanGurebo/UITextFieldLimit
and begin to test!
Mark your storyboard-created UITextField and link it to my subclass using the Identity Inspector:
Then you can link it to an IBOutlet and set the limit(default is 10).
Your ViewController.h file should contain: (if you wan't to modify the setting, like the limit)
#import "UITextFieldLimit.h"
/.../
#property (weak, nonatomic) IBOutlet UITextFieldLimit *textFieldLimit; // <--Your IBOutlet
Your ViewController.m file should #synthesize textFieldLimit.
Set the text length limit in your ViewController.m file:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[textFieldLimit setLimit:25];// <-- and you won't be able to put more than 25 characters in the TextField.
}
Hope the class helps you. Good luck!
This should be enough to solve the problem (replace 4 by the limit u want). Just make sure to add delegate in IB.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSString *newString = [textField.text stringByReplacingCharactersInRange:range withString:string];
return (newString.length<=4);
}
Use below extension to set the maximum character length of a UITextField and UITextView.
Swift 4.0
private var kAssociationKeyMaxLength: Int = 0
private var kAssociationKeyMaxLengthTextView: Int = 0
extension UITextField {
#IBInspectable var maxLength: Int {
get {
if let length = objc_getAssociatedObject(self, &kAssociationKeyMaxLength) as? Int {
return length
} else {
return Int.max
}
}
set {
objc_setAssociatedObject(self, &kAssociationKeyMaxLength, newValue, .OBJC_ASSOCIATION_RETAIN)
addTarget(self, action: #selector(checkMaxLength), for: .editingChanged)
}
}
#objc func checkMaxLength(textField: UITextField) {
guard let prospectiveText = self.text,
prospectiveText.count > maxLength
else {
return
}
let selection = selectedTextRange
let indexEndOfText = prospectiveText.index(prospectiveText.startIndex, offsetBy: maxLength)
let substring = prospectiveText[..<indexEndOfText]
text = String(substring)
selectedTextRange = selection
}
}
UITextView
extension UITextView:UITextViewDelegate {
#IBInspectable var maxLength: Int {
get {
if let length = objc_getAssociatedObject(self, &kAssociationKeyMaxLengthTextView) as? Int {
return length
} else {
return Int.max
}
}
set {
self.delegate = self
objc_setAssociatedObject(self, &kAssociationKeyMaxLengthTextView, newValue, .OBJC_ASSOCIATION_RETAIN)
}
}
public func textViewDidChange(_ textView: UITextView) {
checkMaxLength(textField: self)
}
#objc func checkMaxLength(textField: UITextView) {
guard let prospectiveText = self.text,
prospectiveText.count > maxLength
else {
return
}
let selection = selectedTextRange
let indexEndOfText = prospectiveText.index(prospectiveText.startIndex, offsetBy: maxLength)
let substring = prospectiveText[..<indexEndOfText]
text = String(substring)
selectedTextRange = selection
}
}
You can set limit below.
I simulate the actual string replacement that's about to happen to calculate that future string's length:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSString *newString = [textField.text stringByReplacingCharactersInRange:range withString:string];
if([newString length] > maxLength)
return NO;
return YES;
}
There is generic solution for setting max length in Swift.
By IBInspectable you can add new Attribute in Xcode Attribute Inspector.
import UIKit
private var maxLengths = [UITextField: Int]()
extension UITextField {
#IBInspectable var maxLength: Int {
get {
guard let length = maxLengths[self]
else {
return Int.max
}
return length
}
set {
maxLengths[self] = newValue
addTarget(
self,
action: Selector("limitLength:"),
forControlEvents: UIControlEvents.EditingChanged
)
}
}
func limitLength(textField: UITextField) {
guard let prospectiveText = textField.text
where prospectiveText.characters.count > maxLength else {
return
}
let selection = selectedTextRange
text = prospectiveText.substringWithRange(
Range<String.Index>(prospectiveText.startIndex ..< prospectiveText.startIndex.advancedBy(maxLength))
)
selectedTextRange = selection
}
}
Swift 3 version
//***** This will NOT work with Swift 2.x! *****//
First create a new Swift file : TextFieldMaxLength.swift,
and then add the code below:
import UIKit
private var maxLengths = [UITextField: Int]()
extension UITextField {
#IBInspectable var maxLength: Int {
get {
guard let length = maxLengths[self]
else {
return Int.max
}
return length
}
set {
maxLengths[self] = newValue
addTarget(
self,
action: #selector(limitLength),
for: UIControlEvents.editingChanged
)
}
}
func limitLength(textField: UITextField) {
guard let prospectiveText = textField.text,
prospectiveText.characters.count > maxLength
else {
return
}
let selection = selectedTextRange
let maxCharIndex = prospectiveText.index(prospectiveText.startIndex, offsetBy: maxLength)
text = prospectiveText.substring(to: maxCharIndex)
selectedTextRange = selection
}
}
and then you will see in Storyboard a new field (Max Length) when you select any TextField
if you still have more questions check out this link: http://www.globalnerdy.com/2016/05/18/ios-programming-trick-how-to-use-xcode-to-set-a-text-fields-maximum-length-visual-studio-style/
Using Interface builder you can link and get the event for "Editing changed" in any of your function.
Now there you can put check for the length
- (IBAction)onValueChange:(id)sender
{
NSString *text = nil;
int MAX_LENGTH = 20;
switch ([sender tag] )
{
case 1:
{
text = myEditField.text;
if (MAX_LENGTH < [text length]) {
myEditField.text = [text substringToIndex:MAX_LENGTH];
}
}
break;
default:
break;
}
}
The following code is similar to sickp's answer but handles correctly copy-paste operations. If you try to paste a text that is longer than the limit, the following code will truncate the text to fit the limit instead of refusing the paste operation completely.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
static const NSUInteger limit = 70; // we limit to 70 characters
NSUInteger allowedLength = limit - [textField.text length] + range.length;
if (string.length > allowedLength) {
if (string.length > 1) {
// get at least the part of the new string that fits
NSString *limitedString = [string substringToIndex:allowedLength];
NSMutableString *newString = [textField.text mutableCopy];
[newString replaceCharactersInRange:range withString:limitedString];
textField.text = newString;
}
return NO;
} else {
return YES;
}
}
To make it work with cut & paste of strings of any length, I would suggest changing the function to something like:
#define MAX_LENGTH 20
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSInteger insertDelta = string.length - range.length;
if (textField.text.length + insertDelta > MAX_LENGTH)
{
return NO; // the new string would be longer than MAX_LENGTH
}
else {
return YES;
}
}
Swift 4
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
guard let text = textField.text else { return true }
let newLength = text.count + string.count - range.length
return newLength <= 10
}
Swift 2.0 +
First of all create a class for this process. Lets call it StringValidator.swift.
Then just paste the following code inside it.
import Foundation
extension String {
func containsCharactersIn(matchCharacters: String) -> Bool {
let characterSet = NSCharacterSet(charactersInString: matchCharacters)
return self.rangeOfCharacterFromSet(characterSet) != nil
}
func containsOnlyCharactersIn(matchCharacters: String) -> Bool {
let disallowedCharacterSet = NSCharacterSet(charactersInString: matchCharacters).invertedSet
return self.rangeOfCharacterFromSet(disallowedCharacterSet) == nil
}
func doesNotContainCharactersIn(matchCharacters: String) -> Bool {
let characterSet = NSCharacterSet(charactersInString: matchCharacters)
return self.rangeOfCharacterFromSet(characterSet) == nil
}
func isNumeric() -> Bool
{
let scanner = NSScanner(string: self)
scanner.locale = NSLocale.currentLocale()
return scanner.scanDecimal(nil) && scanner.atEnd
}
}
Now save the class.....
Usage..
Now goto your viewController.swift class and make your textfield's outlets as..
#IBOutlet weak var contactEntryTxtFld: UITextField! //First textfield
#IBOutlet weak var contactEntryTxtFld2: UITextField! //Second textfield
Now goto the textfield's shouldChangeCharactersInRange method and use like the following.
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
if string.characters.count == 0 {
return true
}
let latestText = textField.text ?? ""
let checkAbleText = (latestText as NSString).stringByReplacingCharactersInRange(range, withString: string)
switch textField {
case contactEntryTxtFld:
return checkAbleText.containsOnlyCharactersIn("0123456789") && prospectiveText.characters.count <= 5
case contactEntryTxtFld2:
return checkAbleText.containsOnlyCharactersIn("0123456789") && prospectiveText.characters.count <= 5
default:
return true
}
}
Don't forget to set the delegate protocol/methods of textfields.
Let me explain about this... I am using the simple extension process of string which I wrote inside an another class. Now I am just calling those extension methods from another class where I need them by adding check and maximum value.
Features...
It will set maximum limit of a particular textfield.
It will set type of accepted keys for particular textfield.
Types...
containsOnlyCharactersIn //Accepts only Characters.
containsCharactersIn //Accepts combination of characters
doesNotContainsCharactersIn //Will not accept characters
Hope this helped....
Thanks..
swift 3.0
This code is working fine when you are paste string more than your character limits.
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
let str = (textView.text + text)
if str.characters.count <= 10 {
return true
}
textView.text = str.substring(to: str.index(str.startIndex, offsetBy: 10))
return false
}
Thanks for your votes. :)
I give a supplementary answer based on #Frouo. I think his answer is the most beautiful way. Becuase it's a common control we can reuse.
private var kAssociationKeyMaxLength: Int = 0
extension UITextField {
#IBInspectable var maxLength: Int {
get {
if let length = objc_getAssociatedObject(self, &kAssociationKeyMaxLength) as? Int {
return length
} else {
return Int.max
}
}
set {
objc_setAssociatedObject(self, &kAssociationKeyMaxLength, newValue, .OBJC_ASSOCIATION_RETAIN)
self.addTarget(self, action: #selector(checkMaxLength), for: .editingChanged)
}
}
func checkMaxLength(textField: UITextField) {
guard !self.isInputMethod(), let prospectiveText = self.text,
prospectiveText.count > maxLength
else {
return
}
let selection = selectedTextRange
let maxCharIndex = prospectiveText.index(prospectiveText.startIndex, offsetBy: maxLength)
text = prospectiveText.substring(to: maxCharIndex)
selectedTextRange = selection
}
//The method is used to cancel the check when use Chinese Pinyin input method.
//Becuase the alphabet also appears in the textfield when inputting, we should cancel the check.
func isInputMethod() -> Bool {
if let positionRange = self.markedTextRange {
if let _ = self.position(from: positionRange.start, offset: 0) {
return true
}
}
return false
}
}
This is the correct way to handle max length on UITextField, it allows the return key to exit the resign the textfield as first responder and lets the user backspace when they reach the limit
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
int MAX_LENGHT = 5;
if([string isEqualToString:#"\n"])
{
[textField resignFirstResponder];
return FALSE;
}
else if(textField.text.length > MAX_LENGHT-1)
{
if([string isEqualToString:#""] && range.length == 1)
{
return TRUE;
}
else
{
return FALSE;
}
}
else
{
return TRUE;
}
}
What about this simple approach. Its working fine for me.
extension UITextField {
func charactersLimit(to:Int) {
if (self.text!.count > to) {
self.deleteBackward()
}
}
}
Then:
someTextField.charactersLimit(to:16)
Other answers do not handle the case where user can paste a long string from clipboard. If I paste a long string it should just be truncated but shown.
Use this in your delegate:
static const NSUInteger maxNoOfCharacters = 5;
-(IBAction)textdidChange:(UITextField * )textField
{
NSString * text = textField.text;
if(text.length > maxNoOfCharacters)
{
text = [text substringWithRange:NSMakeRange(0, maxNoOfCharacters)];
textField.text = text;
}
// use 'text'
}
Got it down to 1 line of code :)
Set your text view's delegate to "self" then add the <UITextViewDelegate> in your .h and the following code in your .m .... you can adjust the number "7" to be whatever you want your MAXIMUM number of characters to be.
-(BOOL)textView:(UITextView *)a shouldChangeTextInRange:(NSRange)b replacementText:(NSString *)c {
return ((a.text.length+c.length<=7)+(c.length<1)+(b.length>=c.length)>0);
}
This code accounts for typing new characters, deleting characters, selecting characters then typing or deleting, selecting characters and cutting, pasting in general, and selecting characters and pasting.
Done!
Alternatively, another cool way to write this code with bit-operations would be
-(BOOL)textView:(UITextView *)a shouldChangeTextInRange:(NSRange)b replacementText:(NSString *)c {
return 0^((a.text.length+c.length<=7)+(c.length<1)+(b.length>=c.length));
}
I have open sourced a UITextField subclass, STATextField, that offers this functionality (and much more) with its maxCharacterLength property.
now how many characters u want just give values
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSUInteger newLength = [textField.text length] + [string length] - range.length;
return (newLength > 25) ? NO : YES;
}
Use this code here RESTRICTED_LENGTH is length you want to restrict for textfield.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if (textField == nameTF) {
int limit = RESTRICTED_LENGTH - 1;
return !([textField.text length]>limit && [string length] > range.length);
}
else
{
return YES;
}
return NO;
}
I did this in Swift for an 8 character limit when using a number pad.
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
return !(textField.text?.characters.count == MAX_LENGTH && string != "")
}
I had to test for string != "" to allow the delete button to work on the number pad, otherwise it wouldn't allow deleting characters in the text field after it reached its max.
For Xamarin:
YourTextField.ShouldChangeCharacters =
delegate(UITextField textField, NSRange range, string replacementString)
{
return (range.Location + replacementString.Length) <= 4; // MaxLength == 4
};
I have implemented a UITextField Extension to add a maxLength property to it.
It's based on Xcode 6 IBInspectables, so you can set the maxLength limit on the Interface builder.
Here is the implementation:
UITextField+MaxLength.h
#import <UIKit/UIKit.h>
#interface UITextField_MaxLength : UITextField<UITextFieldDelegate>
#property (nonatomic)IBInspectable int textMaxLength;
#end
UITextField+MaxLength.m
#import "UITextField+MaxLength.h"
#interface UITextField_MaxLength()
#property (nonatomic, assign) id <UITextFieldDelegate> superDelegate;
#end
#implementation UITextField_MaxLength
- (BOOL)textField:(UITextField *) textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
//validate the length, only if it's set to a non zero value
if (self.textMaxLength>0) {
if(range.length + range.location > textField.text.length)
return NO;
if (textField.text.length+string.length - range.length>self.textMaxLength) {
return NO;
}
}
//if length validation was passed, query the super class to see if the delegate method is implemented there
if (self.superDelegate && [self.superDelegate respondsToSelector:#selector(textField:shouldChangeCharactersInRange:replacementString:)]) {
return [self.superDelegate textField:textField shouldChangeCharactersInRange:range replacementString:string];
}
else{
//if the super class does not implement the delegate method, simply return YES as the length validation was passed
return YES;
}
}
- (void)setDelegate:(id<UITextFieldDelegate>)delegate {
if (delegate == self)
return;
self.superDelegate = delegate;
[super setDelegate:self];
}
//forward all non overriden delegate methods
- (id)forwardingTargetForSelector:(SEL)aSelector {
if ([self.superDelegate respondsToSelector:aSelector])
return self.superDelegate;
return [super forwardingTargetForSelector:aSelector];
}
- (BOOL)respondsToSelector:(SEL)aSelector {
if ([self.superDelegate respondsToSelector:aSelector])
return YES;
return [super respondsToSelector:aSelector];
}
#end
How can I set the maximum amount of characters in a UITextField on the iPhone SDK when I load up a UIView?
While the UITextField class has no max length property, it's relatively simple to get this functionality by setting the text field's delegate and implementing the following delegate method:
Objective-C
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
// Prevent crashing undo bug – see note below.
if(range.length + range.location > textField.text.length)
{
return NO;
}
NSUInteger newLength = [textField.text length] + [string length] - range.length;
return newLength <= 25;
}
Swift
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let currentCharacterCount = textField.text?.count ?? 0
if range.length + range.location > currentCharacterCount {
return false
}
let newLength = currentCharacterCount + string.count - range.length
return newLength <= 25
}
Before the text field changes, the UITextField asks the delegate if the specified text should be changed. The text field has not changed at this point, so we grab it's current length and the string length we're inserting (either through pasting copied text or typing a single character using the keyboard), minus the range length. If this value is too long (more than 25 characters in this example), return NO to prohibit the change.
When typing in a single character at the end of a text field, the range.location will be the current field's length, and range.length will be 0 because we're not replacing/deleting anything. Inserting into the middle of a text field just means a different range.location, and pasting multiple characters just means string has more than one character in it.
Deleting single characters or cutting multiple characters is specified by a range with a non-zero length, and an empty string. Replacement is just a range deletion with a non-empty string.
A note on the crashing "undo" bug
As is mentioned in the comments, there is a bug with UITextField that can lead to a crash.
If you paste in to the field, but the paste is prevented by your validation implementation, the paste operation is still recorded in the application's undo buffer. If you then fire an undo (by shaking the device and confirming an Undo), the UITextField will attempt to replace the string it thinks it pasted in to itself with an empty string. This will crash because it never actually pasted the string in to itself. It will try to replace a part of the string that doesn't exist.
Fortunately you can protect the UITextField from killing itself like this. You just need to ensure that the range it proposes to replace does exist within its current string. This is what the initial sanity check above does.
swift 3.0 with copy and paste working fine.
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
let str = (textView.text + text)
if str.characters.count <= 10 {
return true
}
textView.text = str.substring(to: str.index(str.startIndex, offsetBy: 10))
return false
}
Hope it's helpful to you.
Swift 4
import UIKit
private var kAssociationKeyMaxLength: Int = 0
extension UITextField {
#IBInspectable var maxLength: Int {
get {
if let length = objc_getAssociatedObject(self, &kAssociationKeyMaxLength) as? Int {
return length
} else {
return Int.max
}
}
set {
objc_setAssociatedObject(self, &kAssociationKeyMaxLength, newValue, .OBJC_ASSOCIATION_RETAIN)
addTarget(self, action: #selector(checkMaxLength), for: .editingChanged)
}
}
#objc func checkMaxLength(textField: UITextField) {
guard let prospectiveText = self.text,
prospectiveText.count > maxLength
else {
return
}
let selection = selectedTextRange
let indexEndOfText = prospectiveText.index(prospectiveText.startIndex, offsetBy: maxLength)
let substring = prospectiveText[..<indexEndOfText]
text = String(substring)
selectedTextRange = selection
}
}
Edit: memory leak issue fixed.
Thank you august! (Post)
This is the code that I ended up with which works:
#define MAX_LENGTH 20
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if (textField.text.length >= MAX_LENGTH && range.length == 0)
{
return NO; // return NO to not change text
}
else
{return YES;}
}
To complete August answer, a possible implementation of the proposed function (see UITextField's delegate).
I did not test domness code but mine do not get stuck if the user reached the limit and it is compatible with a new string that comes replacing a smaller or equal one.
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
//limit the size :
int limit = 20;
return !([textField.text length]>limit && [string length] > range.length);
}
You can't do this directly - UITextField has no maxLength attribute, but you can set the UITextField's delegate, then use:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
Often you have multiple input fields with a different length.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
int allowedLength;
switch(textField.tag) {
case 1:
allowedLength = MAXLENGTHNAME; // triggered for input fields with tag = 1
break;
case 2:
allowedLength = MAXLENGTHADDRESS; // triggered for input fields with tag = 2
break;
default:
allowedLength = MAXLENGTHDEFAULT; // length default when no tag (=0) value =255
break;
}
if (textField.text.length >= allowedLength && range.length == 0) {
return NO; // Change not allowed
} else {
return YES; // Change allowed
}
}
The best way would be to set up a notification on the text changing. In your -awakeFromNib of your view controller method you'll want:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(limitTextField:) name:#"UITextFieldTextDidChangeNotification" object:myTextField];
Then in the same class add:
- (void)limitTextField:(NSNotification *)note {
int limit = 20;
if ([[myTextField stringValue] length] > limit) {
[myTextField setStringValue:[[myTextField stringValue] substringToIndex:limit]];
}
}
Then link up the outlet myTextField to your UITextField and it will not let you add any more characters after you hit the limit. Be sure to add this to your dealloc method:
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"UITextFieldTextDidChangeNotification" object:myTextField];
I created this UITextFieldLimit subclass:
Multiple textfields supported
Set the text length limit
Paste prevention
Displays a label of left characters inside the textfield, get hidden when you stop editing.
Shake animation when no characters left.
Grab the UITextFieldLimit.h and UITextFieldLimit.m from this GitHub repository:
https://github.com/JonathanGurebo/UITextFieldLimit
and begin to test!
Mark your storyboard-created UITextField and link it to my subclass using the Identity Inspector:
Then you can link it to an IBOutlet and set the limit(default is 10).
Your ViewController.h file should contain: (if you wan't to modify the setting, like the limit)
#import "UITextFieldLimit.h"
/.../
#property (weak, nonatomic) IBOutlet UITextFieldLimit *textFieldLimit; // <--Your IBOutlet
Your ViewController.m file should #synthesize textFieldLimit.
Set the text length limit in your ViewController.m file:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[textFieldLimit setLimit:25];// <-- and you won't be able to put more than 25 characters in the TextField.
}
Hope the class helps you. Good luck!
This should be enough to solve the problem (replace 4 by the limit u want). Just make sure to add delegate in IB.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSString *newString = [textField.text stringByReplacingCharactersInRange:range withString:string];
return (newString.length<=4);
}
Use below extension to set the maximum character length of a UITextField and UITextView.
Swift 4.0
private var kAssociationKeyMaxLength: Int = 0
private var kAssociationKeyMaxLengthTextView: Int = 0
extension UITextField {
#IBInspectable var maxLength: Int {
get {
if let length = objc_getAssociatedObject(self, &kAssociationKeyMaxLength) as? Int {
return length
} else {
return Int.max
}
}
set {
objc_setAssociatedObject(self, &kAssociationKeyMaxLength, newValue, .OBJC_ASSOCIATION_RETAIN)
addTarget(self, action: #selector(checkMaxLength), for: .editingChanged)
}
}
#objc func checkMaxLength(textField: UITextField) {
guard let prospectiveText = self.text,
prospectiveText.count > maxLength
else {
return
}
let selection = selectedTextRange
let indexEndOfText = prospectiveText.index(prospectiveText.startIndex, offsetBy: maxLength)
let substring = prospectiveText[..<indexEndOfText]
text = String(substring)
selectedTextRange = selection
}
}
UITextView
extension UITextView:UITextViewDelegate {
#IBInspectable var maxLength: Int {
get {
if let length = objc_getAssociatedObject(self, &kAssociationKeyMaxLengthTextView) as? Int {
return length
} else {
return Int.max
}
}
set {
self.delegate = self
objc_setAssociatedObject(self, &kAssociationKeyMaxLengthTextView, newValue, .OBJC_ASSOCIATION_RETAIN)
}
}
public func textViewDidChange(_ textView: UITextView) {
checkMaxLength(textField: self)
}
#objc func checkMaxLength(textField: UITextView) {
guard let prospectiveText = self.text,
prospectiveText.count > maxLength
else {
return
}
let selection = selectedTextRange
let indexEndOfText = prospectiveText.index(prospectiveText.startIndex, offsetBy: maxLength)
let substring = prospectiveText[..<indexEndOfText]
text = String(substring)
selectedTextRange = selection
}
}
You can set limit below.
I simulate the actual string replacement that's about to happen to calculate that future string's length:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSString *newString = [textField.text stringByReplacingCharactersInRange:range withString:string];
if([newString length] > maxLength)
return NO;
return YES;
}
There is generic solution for setting max length in Swift.
By IBInspectable you can add new Attribute in Xcode Attribute Inspector.
import UIKit
private var maxLengths = [UITextField: Int]()
extension UITextField {
#IBInspectable var maxLength: Int {
get {
guard let length = maxLengths[self]
else {
return Int.max
}
return length
}
set {
maxLengths[self] = newValue
addTarget(
self,
action: Selector("limitLength:"),
forControlEvents: UIControlEvents.EditingChanged
)
}
}
func limitLength(textField: UITextField) {
guard let prospectiveText = textField.text
where prospectiveText.characters.count > maxLength else {
return
}
let selection = selectedTextRange
text = prospectiveText.substringWithRange(
Range<String.Index>(prospectiveText.startIndex ..< prospectiveText.startIndex.advancedBy(maxLength))
)
selectedTextRange = selection
}
}
Swift 3 version
//***** This will NOT work with Swift 2.x! *****//
First create a new Swift file : TextFieldMaxLength.swift,
and then add the code below:
import UIKit
private var maxLengths = [UITextField: Int]()
extension UITextField {
#IBInspectable var maxLength: Int {
get {
guard let length = maxLengths[self]
else {
return Int.max
}
return length
}
set {
maxLengths[self] = newValue
addTarget(
self,
action: #selector(limitLength),
for: UIControlEvents.editingChanged
)
}
}
func limitLength(textField: UITextField) {
guard let prospectiveText = textField.text,
prospectiveText.characters.count > maxLength
else {
return
}
let selection = selectedTextRange
let maxCharIndex = prospectiveText.index(prospectiveText.startIndex, offsetBy: maxLength)
text = prospectiveText.substring(to: maxCharIndex)
selectedTextRange = selection
}
}
and then you will see in Storyboard a new field (Max Length) when you select any TextField
if you still have more questions check out this link: http://www.globalnerdy.com/2016/05/18/ios-programming-trick-how-to-use-xcode-to-set-a-text-fields-maximum-length-visual-studio-style/
Using Interface builder you can link and get the event for "Editing changed" in any of your function.
Now there you can put check for the length
- (IBAction)onValueChange:(id)sender
{
NSString *text = nil;
int MAX_LENGTH = 20;
switch ([sender tag] )
{
case 1:
{
text = myEditField.text;
if (MAX_LENGTH < [text length]) {
myEditField.text = [text substringToIndex:MAX_LENGTH];
}
}
break;
default:
break;
}
}
The following code is similar to sickp's answer but handles correctly copy-paste operations. If you try to paste a text that is longer than the limit, the following code will truncate the text to fit the limit instead of refusing the paste operation completely.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
static const NSUInteger limit = 70; // we limit to 70 characters
NSUInteger allowedLength = limit - [textField.text length] + range.length;
if (string.length > allowedLength) {
if (string.length > 1) {
// get at least the part of the new string that fits
NSString *limitedString = [string substringToIndex:allowedLength];
NSMutableString *newString = [textField.text mutableCopy];
[newString replaceCharactersInRange:range withString:limitedString];
textField.text = newString;
}
return NO;
} else {
return YES;
}
}
To make it work with cut & paste of strings of any length, I would suggest changing the function to something like:
#define MAX_LENGTH 20
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSInteger insertDelta = string.length - range.length;
if (textField.text.length + insertDelta > MAX_LENGTH)
{
return NO; // the new string would be longer than MAX_LENGTH
}
else {
return YES;
}
}
Swift 4
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
guard let text = textField.text else { return true }
let newLength = text.count + string.count - range.length
return newLength <= 10
}
Swift 2.0 +
First of all create a class for this process. Lets call it StringValidator.swift.
Then just paste the following code inside it.
import Foundation
extension String {
func containsCharactersIn(matchCharacters: String) -> Bool {
let characterSet = NSCharacterSet(charactersInString: matchCharacters)
return self.rangeOfCharacterFromSet(characterSet) != nil
}
func containsOnlyCharactersIn(matchCharacters: String) -> Bool {
let disallowedCharacterSet = NSCharacterSet(charactersInString: matchCharacters).invertedSet
return self.rangeOfCharacterFromSet(disallowedCharacterSet) == nil
}
func doesNotContainCharactersIn(matchCharacters: String) -> Bool {
let characterSet = NSCharacterSet(charactersInString: matchCharacters)
return self.rangeOfCharacterFromSet(characterSet) == nil
}
func isNumeric() -> Bool
{
let scanner = NSScanner(string: self)
scanner.locale = NSLocale.currentLocale()
return scanner.scanDecimal(nil) && scanner.atEnd
}
}
Now save the class.....
Usage..
Now goto your viewController.swift class and make your textfield's outlets as..
#IBOutlet weak var contactEntryTxtFld: UITextField! //First textfield
#IBOutlet weak var contactEntryTxtFld2: UITextField! //Second textfield
Now goto the textfield's shouldChangeCharactersInRange method and use like the following.
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
if string.characters.count == 0 {
return true
}
let latestText = textField.text ?? ""
let checkAbleText = (latestText as NSString).stringByReplacingCharactersInRange(range, withString: string)
switch textField {
case contactEntryTxtFld:
return checkAbleText.containsOnlyCharactersIn("0123456789") && prospectiveText.characters.count <= 5
case contactEntryTxtFld2:
return checkAbleText.containsOnlyCharactersIn("0123456789") && prospectiveText.characters.count <= 5
default:
return true
}
}
Don't forget to set the delegate protocol/methods of textfields.
Let me explain about this... I am using the simple extension process of string which I wrote inside an another class. Now I am just calling those extension methods from another class where I need them by adding check and maximum value.
Features...
It will set maximum limit of a particular textfield.
It will set type of accepted keys for particular textfield.
Types...
containsOnlyCharactersIn //Accepts only Characters.
containsCharactersIn //Accepts combination of characters
doesNotContainsCharactersIn //Will not accept characters
Hope this helped....
Thanks..
swift 3.0
This code is working fine when you are paste string more than your character limits.
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
let str = (textView.text + text)
if str.characters.count <= 10 {
return true
}
textView.text = str.substring(to: str.index(str.startIndex, offsetBy: 10))
return false
}
Thanks for your votes. :)
I give a supplementary answer based on #Frouo. I think his answer is the most beautiful way. Becuase it's a common control we can reuse.
private var kAssociationKeyMaxLength: Int = 0
extension UITextField {
#IBInspectable var maxLength: Int {
get {
if let length = objc_getAssociatedObject(self, &kAssociationKeyMaxLength) as? Int {
return length
} else {
return Int.max
}
}
set {
objc_setAssociatedObject(self, &kAssociationKeyMaxLength, newValue, .OBJC_ASSOCIATION_RETAIN)
self.addTarget(self, action: #selector(checkMaxLength), for: .editingChanged)
}
}
func checkMaxLength(textField: UITextField) {
guard !self.isInputMethod(), let prospectiveText = self.text,
prospectiveText.count > maxLength
else {
return
}
let selection = selectedTextRange
let maxCharIndex = prospectiveText.index(prospectiveText.startIndex, offsetBy: maxLength)
text = prospectiveText.substring(to: maxCharIndex)
selectedTextRange = selection
}
//The method is used to cancel the check when use Chinese Pinyin input method.
//Becuase the alphabet also appears in the textfield when inputting, we should cancel the check.
func isInputMethod() -> Bool {
if let positionRange = self.markedTextRange {
if let _ = self.position(from: positionRange.start, offset: 0) {
return true
}
}
return false
}
}
This is the correct way to handle max length on UITextField, it allows the return key to exit the resign the textfield as first responder and lets the user backspace when they reach the limit
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
int MAX_LENGHT = 5;
if([string isEqualToString:#"\n"])
{
[textField resignFirstResponder];
return FALSE;
}
else if(textField.text.length > MAX_LENGHT-1)
{
if([string isEqualToString:#""] && range.length == 1)
{
return TRUE;
}
else
{
return FALSE;
}
}
else
{
return TRUE;
}
}
What about this simple approach. Its working fine for me.
extension UITextField {
func charactersLimit(to:Int) {
if (self.text!.count > to) {
self.deleteBackward()
}
}
}
Then:
someTextField.charactersLimit(to:16)
Other answers do not handle the case where user can paste a long string from clipboard. If I paste a long string it should just be truncated but shown.
Use this in your delegate:
static const NSUInteger maxNoOfCharacters = 5;
-(IBAction)textdidChange:(UITextField * )textField
{
NSString * text = textField.text;
if(text.length > maxNoOfCharacters)
{
text = [text substringWithRange:NSMakeRange(0, maxNoOfCharacters)];
textField.text = text;
}
// use 'text'
}
Got it down to 1 line of code :)
Set your text view's delegate to "self" then add the <UITextViewDelegate> in your .h and the following code in your .m .... you can adjust the number "7" to be whatever you want your MAXIMUM number of characters to be.
-(BOOL)textView:(UITextView *)a shouldChangeTextInRange:(NSRange)b replacementText:(NSString *)c {
return ((a.text.length+c.length<=7)+(c.length<1)+(b.length>=c.length)>0);
}
This code accounts for typing new characters, deleting characters, selecting characters then typing or deleting, selecting characters and cutting, pasting in general, and selecting characters and pasting.
Done!
Alternatively, another cool way to write this code with bit-operations would be
-(BOOL)textView:(UITextView *)a shouldChangeTextInRange:(NSRange)b replacementText:(NSString *)c {
return 0^((a.text.length+c.length<=7)+(c.length<1)+(b.length>=c.length));
}
I have open sourced a UITextField subclass, STATextField, that offers this functionality (and much more) with its maxCharacterLength property.
now how many characters u want just give values
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSUInteger newLength = [textField.text length] + [string length] - range.length;
return (newLength > 25) ? NO : YES;
}
Use this code here RESTRICTED_LENGTH is length you want to restrict for textfield.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if (textField == nameTF) {
int limit = RESTRICTED_LENGTH - 1;
return !([textField.text length]>limit && [string length] > range.length);
}
else
{
return YES;
}
return NO;
}
I did this in Swift for an 8 character limit when using a number pad.
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
return !(textField.text?.characters.count == MAX_LENGTH && string != "")
}
I had to test for string != "" to allow the delete button to work on the number pad, otherwise it wouldn't allow deleting characters in the text field after it reached its max.
For Xamarin:
YourTextField.ShouldChangeCharacters =
delegate(UITextField textField, NSRange range, string replacementString)
{
return (range.Location + replacementString.Length) <= 4; // MaxLength == 4
};
I have implemented a UITextField Extension to add a maxLength property to it.
It's based on Xcode 6 IBInspectables, so you can set the maxLength limit on the Interface builder.
Here is the implementation:
UITextField+MaxLength.h
#import <UIKit/UIKit.h>
#interface UITextField_MaxLength : UITextField<UITextFieldDelegate>
#property (nonatomic)IBInspectable int textMaxLength;
#end
UITextField+MaxLength.m
#import "UITextField+MaxLength.h"
#interface UITextField_MaxLength()
#property (nonatomic, assign) id <UITextFieldDelegate> superDelegate;
#end
#implementation UITextField_MaxLength
- (BOOL)textField:(UITextField *) textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
//validate the length, only if it's set to a non zero value
if (self.textMaxLength>0) {
if(range.length + range.location > textField.text.length)
return NO;
if (textField.text.length+string.length - range.length>self.textMaxLength) {
return NO;
}
}
//if length validation was passed, query the super class to see if the delegate method is implemented there
if (self.superDelegate && [self.superDelegate respondsToSelector:#selector(textField:shouldChangeCharactersInRange:replacementString:)]) {
return [self.superDelegate textField:textField shouldChangeCharactersInRange:range replacementString:string];
}
else{
//if the super class does not implement the delegate method, simply return YES as the length validation was passed
return YES;
}
}
- (void)setDelegate:(id<UITextFieldDelegate>)delegate {
if (delegate == self)
return;
self.superDelegate = delegate;
[super setDelegate:self];
}
//forward all non overriden delegate methods
- (id)forwardingTargetForSelector:(SEL)aSelector {
if ([self.superDelegate respondsToSelector:aSelector])
return self.superDelegate;
return [super forwardingTargetForSelector:aSelector];
}
- (BOOL)respondsToSelector:(SEL)aSelector {
if ([self.superDelegate respondsToSelector:aSelector])
return YES;
return [super respondsToSelector:aSelector];
}
#end
I have a UITextField that when clicked brings up a number pad with a decimal point in the bottom left. I am trying to limit the field so that a user can only place 1 decimal mark
e.g.
2.5 OK
2..5 NOT OK
Implement the shouldChangeCharactersInRange method like this:
// Only allow one decimal point
// Example assumes ARC - Implement proper memory management if not using.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSString *newString = [textField.text stringByReplacingCharactersInRange:range withString:string];
NSArray *arrayOfString = [newString componentsSeparatedByString:#"."];
if ([arrayOfString count] > 2 )
return NO;
return YES;
}
This creates an array of strings split by the decimal point, so if there is more than one decimal point we will have at least 3 elements in the array.
Here is an example with a regular expression, the example limits to only one decimal point and 2 decimals. You can tweak it to fit your needs.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSString *newString = [textField.text stringByReplacingCharactersInRange:range withString:string];
NSString *expression = #"^[0-9]*((\\.|,)[0-9]{0,2})?$";
NSError *error = nil;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:expression options:NSRegularExpressionCaseInsensitive error:&error];
NSUInteger numberOfMatches = [regex numberOfMatchesInString:newString options:0 range:NSMakeRange(0, [newString length])];
return numberOfMatches != 0;
}
Swift 3 Implement this UITextFieldDelegate method to prevent user from typing an invalid number:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let text = (textField.text ?? "") as NSString
let newText = text.replacingCharacters(in: range, with: string)
if let regex = try? NSRegularExpression(pattern: "^[0-9]*((\\.|,)[0-9]*)?$", options: .caseInsensitive) {
return regex.numberOfMatches(in: newText, options: .reportProgress, range: NSRange(location: 0, length: (newText as NSString).length)) > 0
}
return false
}
It is working with both comma or dot as decimal separator. You can also limit number of fraction digits using this pattern: "^[0-9]*((\\.|,)[0-9]{0,2})?$" (in this case 2).
Swift 4
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
// Allow to remove character (Backspace)
if string == "" {
return true
}
// Block multiple dot
if (textField.text?.contains("."))! && string == "." {
return false
}
// Check here decimal places
if (textField.text?.contains("."))! {
let limitDecimalPlace = 2
let decimalPlace = textField.text?.components(separatedBy: ".").last
if (decimalPlace?.count)! < limitDecimalPlace {
return true
}
else {
return false
}
}
return true
}
Objective-C
//Create this variable in .h file or .m file
float _numberOfDecimal;
//assign value in viewDidLoad method
numberOfDecimal = 2;
#pragma mark - TextFieldDelegate
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
// Allow to remove character (Backspace)
if ([string isEqualToString:#""]) {
return true;
}
// Block multiple dot
if ([textField.text containsString:#"."] && [string isEqualToString:#"."]) {
return false;
}
// Check here decimal places
if ([textField.text containsString:#"."]) {
NSString *strDecimalPlace = [[textField.text componentsSeparatedByString:#"."] lastObject];
if (strDecimalPlace.length < _numberOfDecimal) {
return true;
}
else {
return false;
}
}
return true;
}
For Swift 2.3 to prevent user for enter decimal number after two places -
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool
{
let decimalPlacesLimit = 2
let rangeDot = txtPrice.text!.rangeOfString(".", options: .CaseInsensitiveSearch)
if rangeDot?.count > 0
{
if (string == ".")
{
print("textField already contains a separator")
return false
}
else {
var explodedString = txtPrice.text!.componentsSeparatedByString(".")
let decimalPart = explodedString[1]
if decimalPart.characters.count >= decimalPlacesLimit && !(string == "")
{
print("textField already contains \(decimalPlacesLimit) decimal places")
return false
}
}
}
}
Building on the accepted answer, the following approach validates three cases that are helpful when dealing with money formats:
Extremely large amounts
More than 2 characters after the decimal point
More than 1 decimal points
Make sure your text field's delegate is set properly, your class conforms to the UITextField protocol, and add the following delegate method.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
// Check for deletion of the $ sign
if (range.location == 0 && [textField.text hasPrefix:#"$"])
return NO;
NSString *updatedText = [textField.text stringByReplacingCharactersInRange:range withString:string];
NSArray *stringsArray = [updatedText componentsSeparatedByString:#"."];
// Check for an absurdly large amount
if (stringsArray.count > 0)
{
NSString *dollarAmount = stringsArray[0];
if (dollarAmount.length > 6)
return NO;
}
// Check for more than 2 chars after the decimal point
if (stringsArray.count > 1)
{
NSString *centAmount = stringsArray[1];
if (centAmount.length > 2)
return NO;
}
// Check for a second decimal point
if (stringsArray.count > 2)
return NO;
return YES;
}
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if(textField == min_textfield )
{
if([textField.text rangeOfString:#"."].location == NSNotFound)
{
if([string isEqualToString:#"."] )
{
flag_for_text = 1;
}
else
{
textField.text = [NSMutableString stringWithFormat:#"%#",textField.text];
}
}
else
{
if([string isEqualToString:#"."])
{
return NO;
}
else
{
textField.text = [NSMutableString stringWithFormat:#"%#",textField.text];
}
}
}
}
Try this :-
public func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
if(text == "," || text == "." ){
let countdots = textView.text!.componentsSeparatedByString(".").count - 1
if countdots > 0 && (text == "." || text == "," )
{
return false
}
}
return true
}
Swift 3
No need to create an array and check count. Limit user can only place 1 decimal mark like this.
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if (textField.text?.contains("."))! && string.contains(".")
{
return false
}
else
{
return true
}
}
Swift 4
max number of Integers Numbers is 4 i.e., 9999, and max decimal digits limit is 2. So, max number can be 9999.99
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
// 100 is the tag value of our textfield
/*or you may use "if textfield == myTextField{" if you have an IBOutlet to that textfield */
if textField.tag == 100 {
//max length limit of text is 8
if textField.text!.count > 8 && string != "" {
return false
}
let maxLength = 8
let currentString: NSString = textField.text! as NSString
// Use following code If you are inputting price to that text field and want $ to get inserted automatically at start when user starts typing in that textfield or you may put some other character at start instead of $. Otherwise comment the following 3 lines of if condition code
if currentString.length == 0 {
priceTextField.text = "$"
}
//new string after inserting the new entered characters
let newString: NSString =
currentString.replacingCharacters(in: range, with: string) as NSString
if newString.length > maxLength{
return false
}
if (textField.text!.range(of: ".") != nil) {
let numStr = newString.components(separatedBy: ".")
if numStr.count>1{
let decStr = numStr[1]
if decStr.length > 2{
return false
}
}
}
var priceStr: String = newString as String
if (textField.text!.range(of: "$") != nil) {
priceStr = priceStr.replacingOccurrences(of: "$", with: "")
}
let price: Double = Double(priceStr) ?? 0
if price > 9999.99{
return false
}
switch string {
case "0","1","2","3","4","5","6","7","8","9":
return true
case ".":
let array = Array(textField.text!)
var decimalCount = 0
for character in array {
if character == "." {
decimalCount = decimalCount + 1
}
}
if decimalCount == 1 {
return false
} else {
return true
}
default:
let array = Array(string)
if array.count == 0 {
return true
}
return false
}
}
return true
}
SWIFT 5
Improvement
Info : do not allow :
separator at the beginning
zero plus another digit at the start except when you add a separator after
1: set the keyboard type to : Decimal Pad
2: copy past
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
//!\ set the keyboard type to : Decimal Pad /!\\
// CUSTOM SETUP
let c = NSLocale.current.decimalSeparator ?? "."
let limitBeforeSeparator = 2
let limitAfterSeparator = 2
// ---------
var validatorUserInput:Bool = false
let text = (textField.text ?? "") as NSString
let newText = text.replacingCharacters(in: range, with: string)
// Validator
let pattern = "(?!0[0-9])\\d*(?!\\\(c))^[0-9]{0,\(limitBeforeSeparator)}((\\\(c))[0-9]{0,\(limitAfterSeparator)})?$"
if let regex = try? NSRegularExpression(pattern: pattern, options: .caseInsensitive) {
validatorUserInput = regex.numberOfMatches(in: newText, options: .reportProgress, range: NSRange(location: 0, length: (newText as NSString).length)) > 0
}
if validatorUserInput {
// setting data or something eles before the return
if let char = string.cString(using: String.Encoding.utf8) {
let isBackSpace = strcmp(char, "\\b")
if (isBackSpace == -92 && textField.text?.count == 1) {
print("Backspace was pressed")
print(newText)
// do something...
} else {
print("Number Added")
print(newText)
// do something...
}
}
return validatorUserInput
} else {
return validatorUserInput
}
}
3: set in the method, if you want x maximum number of digits before and after the separator
let limitBeforeSeparator = 2
let limitAfterSeparator = 2
In whatever object you set your UITextField's delegate to, add a method that answers to "[- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string]".
Then you can either use a NSNumberFormatter object or you can brute force check for an already existing decimal place mark (returning NO if a decimal mark already exists).
Short told, the number format is as follows [NSString stringWithFormat:#"%9.5f", x]; Where 5 is the decimal after ",".
I made the solution, that brings you control over decimal places count, so user can type only one decimal separator and you can also have a control over decimal places count.
Just set the decimalPlacesLimit value properly.
See the method:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSLog(#"text on the way: %#", string);
NSUInteger decimalPlacesLimit = 2;
NSRange rangeDot = [textField.text rangeOfString:#"." options:NSCaseInsensitiveSearch];
NSRange rangeComma = [textField.text rangeOfString:#"," options:NSCaseInsensitiveSearch];
if (rangeDot.length > 0 || rangeComma.length > 0){
if([string isEqualToString:#"."]) {
NSLog(#"textField already contains a separator");
return NO;
} else {
NSArray *explodedString = [textField.text componentsSeparatedByString:#"."];
NSString *decimalPart = explodedString[1];
if (decimalPart.length >= decimalPlacesLimit && ![string isEqualToString:#""]) {
NSLog(#"textField already contains %d decimal places", decimalPlacesLimit);
return NO;
}
}
}
return YES;
}
Swift 4
The efficient and easy way to avoid multiple decimal points (. or ,) in UITextField:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if(string == "," || string == "." ){
if ((textField.text?.contains(","))! || (textField.text?.contains("."))!){
return false
}
}
return true
}
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
if([string isEqualToString:#"."]) {
BOOL containsDecimal = [textField.text containsString:#"."];
return !containsDecimal;
}
return YES;
}
If textfield text already contains a '.' then return NO else return YES.
Thanks everyone, I needed to limit the number before and after the dot. It was only with a regular expression that this became possible for me.
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if string == "," {
textField.text = textField.text! + "."
return false
}
guard let newTextField = textField.text else { return false }
if !string.isEmpty {
let text = newTextField as NSString
let newText = text.replacingCharacters(in: range, with: string)
if let regex = try? NSRegularExpression(pattern: "^[0-9]{0,4}$*((\\.|,)[0-9]{0,4})?$", options: .caseInsensitive) {
return regex.numberOfMatches(in: newText, options: .reportProgress, range: NSRange(location: 0, length: (newText as NSString).length)) > 0
}
return false
}
return true
// #"^[0-9]{0,3}$*((\\.|,)[0-9]{0,2})?$"
}
How can I set the maximum amount of characters in a UITextField on the iPhone SDK when I load up a UIView?
While the UITextField class has no max length property, it's relatively simple to get this functionality by setting the text field's delegate and implementing the following delegate method:
Objective-C
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
// Prevent crashing undo bug – see note below.
if(range.length + range.location > textField.text.length)
{
return NO;
}
NSUInteger newLength = [textField.text length] + [string length] - range.length;
return newLength <= 25;
}
Swift
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let currentCharacterCount = textField.text?.count ?? 0
if range.length + range.location > currentCharacterCount {
return false
}
let newLength = currentCharacterCount + string.count - range.length
return newLength <= 25
}
Before the text field changes, the UITextField asks the delegate if the specified text should be changed. The text field has not changed at this point, so we grab it's current length and the string length we're inserting (either through pasting copied text or typing a single character using the keyboard), minus the range length. If this value is too long (more than 25 characters in this example), return NO to prohibit the change.
When typing in a single character at the end of a text field, the range.location will be the current field's length, and range.length will be 0 because we're not replacing/deleting anything. Inserting into the middle of a text field just means a different range.location, and pasting multiple characters just means string has more than one character in it.
Deleting single characters or cutting multiple characters is specified by a range with a non-zero length, and an empty string. Replacement is just a range deletion with a non-empty string.
A note on the crashing "undo" bug
As is mentioned in the comments, there is a bug with UITextField that can lead to a crash.
If you paste in to the field, but the paste is prevented by your validation implementation, the paste operation is still recorded in the application's undo buffer. If you then fire an undo (by shaking the device and confirming an Undo), the UITextField will attempt to replace the string it thinks it pasted in to itself with an empty string. This will crash because it never actually pasted the string in to itself. It will try to replace a part of the string that doesn't exist.
Fortunately you can protect the UITextField from killing itself like this. You just need to ensure that the range it proposes to replace does exist within its current string. This is what the initial sanity check above does.
swift 3.0 with copy and paste working fine.
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
let str = (textView.text + text)
if str.characters.count <= 10 {
return true
}
textView.text = str.substring(to: str.index(str.startIndex, offsetBy: 10))
return false
}
Hope it's helpful to you.
Swift 4
import UIKit
private var kAssociationKeyMaxLength: Int = 0
extension UITextField {
#IBInspectable var maxLength: Int {
get {
if let length = objc_getAssociatedObject(self, &kAssociationKeyMaxLength) as? Int {
return length
} else {
return Int.max
}
}
set {
objc_setAssociatedObject(self, &kAssociationKeyMaxLength, newValue, .OBJC_ASSOCIATION_RETAIN)
addTarget(self, action: #selector(checkMaxLength), for: .editingChanged)
}
}
#objc func checkMaxLength(textField: UITextField) {
guard let prospectiveText = self.text,
prospectiveText.count > maxLength
else {
return
}
let selection = selectedTextRange
let indexEndOfText = prospectiveText.index(prospectiveText.startIndex, offsetBy: maxLength)
let substring = prospectiveText[..<indexEndOfText]
text = String(substring)
selectedTextRange = selection
}
}
Edit: memory leak issue fixed.
Thank you august! (Post)
This is the code that I ended up with which works:
#define MAX_LENGTH 20
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if (textField.text.length >= MAX_LENGTH && range.length == 0)
{
return NO; // return NO to not change text
}
else
{return YES;}
}
To complete August answer, a possible implementation of the proposed function (see UITextField's delegate).
I did not test domness code but mine do not get stuck if the user reached the limit and it is compatible with a new string that comes replacing a smaller or equal one.
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
//limit the size :
int limit = 20;
return !([textField.text length]>limit && [string length] > range.length);
}
You can't do this directly - UITextField has no maxLength attribute, but you can set the UITextField's delegate, then use:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
Often you have multiple input fields with a different length.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
int allowedLength;
switch(textField.tag) {
case 1:
allowedLength = MAXLENGTHNAME; // triggered for input fields with tag = 1
break;
case 2:
allowedLength = MAXLENGTHADDRESS; // triggered for input fields with tag = 2
break;
default:
allowedLength = MAXLENGTHDEFAULT; // length default when no tag (=0) value =255
break;
}
if (textField.text.length >= allowedLength && range.length == 0) {
return NO; // Change not allowed
} else {
return YES; // Change allowed
}
}
The best way would be to set up a notification on the text changing. In your -awakeFromNib of your view controller method you'll want:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(limitTextField:) name:#"UITextFieldTextDidChangeNotification" object:myTextField];
Then in the same class add:
- (void)limitTextField:(NSNotification *)note {
int limit = 20;
if ([[myTextField stringValue] length] > limit) {
[myTextField setStringValue:[[myTextField stringValue] substringToIndex:limit]];
}
}
Then link up the outlet myTextField to your UITextField and it will not let you add any more characters after you hit the limit. Be sure to add this to your dealloc method:
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"UITextFieldTextDidChangeNotification" object:myTextField];
I created this UITextFieldLimit subclass:
Multiple textfields supported
Set the text length limit
Paste prevention
Displays a label of left characters inside the textfield, get hidden when you stop editing.
Shake animation when no characters left.
Grab the UITextFieldLimit.h and UITextFieldLimit.m from this GitHub repository:
https://github.com/JonathanGurebo/UITextFieldLimit
and begin to test!
Mark your storyboard-created UITextField and link it to my subclass using the Identity Inspector:
Then you can link it to an IBOutlet and set the limit(default is 10).
Your ViewController.h file should contain: (if you wan't to modify the setting, like the limit)
#import "UITextFieldLimit.h"
/.../
#property (weak, nonatomic) IBOutlet UITextFieldLimit *textFieldLimit; // <--Your IBOutlet
Your ViewController.m file should #synthesize textFieldLimit.
Set the text length limit in your ViewController.m file:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[textFieldLimit setLimit:25];// <-- and you won't be able to put more than 25 characters in the TextField.
}
Hope the class helps you. Good luck!
This should be enough to solve the problem (replace 4 by the limit u want). Just make sure to add delegate in IB.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSString *newString = [textField.text stringByReplacingCharactersInRange:range withString:string];
return (newString.length<=4);
}
Use below extension to set the maximum character length of a UITextField and UITextView.
Swift 4.0
private var kAssociationKeyMaxLength: Int = 0
private var kAssociationKeyMaxLengthTextView: Int = 0
extension UITextField {
#IBInspectable var maxLength: Int {
get {
if let length = objc_getAssociatedObject(self, &kAssociationKeyMaxLength) as? Int {
return length
} else {
return Int.max
}
}
set {
objc_setAssociatedObject(self, &kAssociationKeyMaxLength, newValue, .OBJC_ASSOCIATION_RETAIN)
addTarget(self, action: #selector(checkMaxLength), for: .editingChanged)
}
}
#objc func checkMaxLength(textField: UITextField) {
guard let prospectiveText = self.text,
prospectiveText.count > maxLength
else {
return
}
let selection = selectedTextRange
let indexEndOfText = prospectiveText.index(prospectiveText.startIndex, offsetBy: maxLength)
let substring = prospectiveText[..<indexEndOfText]
text = String(substring)
selectedTextRange = selection
}
}
UITextView
extension UITextView:UITextViewDelegate {
#IBInspectable var maxLength: Int {
get {
if let length = objc_getAssociatedObject(self, &kAssociationKeyMaxLengthTextView) as? Int {
return length
} else {
return Int.max
}
}
set {
self.delegate = self
objc_setAssociatedObject(self, &kAssociationKeyMaxLengthTextView, newValue, .OBJC_ASSOCIATION_RETAIN)
}
}
public func textViewDidChange(_ textView: UITextView) {
checkMaxLength(textField: self)
}
#objc func checkMaxLength(textField: UITextView) {
guard let prospectiveText = self.text,
prospectiveText.count > maxLength
else {
return
}
let selection = selectedTextRange
let indexEndOfText = prospectiveText.index(prospectiveText.startIndex, offsetBy: maxLength)
let substring = prospectiveText[..<indexEndOfText]
text = String(substring)
selectedTextRange = selection
}
}
You can set limit below.
I simulate the actual string replacement that's about to happen to calculate that future string's length:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSString *newString = [textField.text stringByReplacingCharactersInRange:range withString:string];
if([newString length] > maxLength)
return NO;
return YES;
}
There is generic solution for setting max length in Swift.
By IBInspectable you can add new Attribute in Xcode Attribute Inspector.
import UIKit
private var maxLengths = [UITextField: Int]()
extension UITextField {
#IBInspectable var maxLength: Int {
get {
guard let length = maxLengths[self]
else {
return Int.max
}
return length
}
set {
maxLengths[self] = newValue
addTarget(
self,
action: Selector("limitLength:"),
forControlEvents: UIControlEvents.EditingChanged
)
}
}
func limitLength(textField: UITextField) {
guard let prospectiveText = textField.text
where prospectiveText.characters.count > maxLength else {
return
}
let selection = selectedTextRange
text = prospectiveText.substringWithRange(
Range<String.Index>(prospectiveText.startIndex ..< prospectiveText.startIndex.advancedBy(maxLength))
)
selectedTextRange = selection
}
}
Swift 3 version
//***** This will NOT work with Swift 2.x! *****//
First create a new Swift file : TextFieldMaxLength.swift,
and then add the code below:
import UIKit
private var maxLengths = [UITextField: Int]()
extension UITextField {
#IBInspectable var maxLength: Int {
get {
guard let length = maxLengths[self]
else {
return Int.max
}
return length
}
set {
maxLengths[self] = newValue
addTarget(
self,
action: #selector(limitLength),
for: UIControlEvents.editingChanged
)
}
}
func limitLength(textField: UITextField) {
guard let prospectiveText = textField.text,
prospectiveText.characters.count > maxLength
else {
return
}
let selection = selectedTextRange
let maxCharIndex = prospectiveText.index(prospectiveText.startIndex, offsetBy: maxLength)
text = prospectiveText.substring(to: maxCharIndex)
selectedTextRange = selection
}
}
and then you will see in Storyboard a new field (Max Length) when you select any TextField
if you still have more questions check out this link: http://www.globalnerdy.com/2016/05/18/ios-programming-trick-how-to-use-xcode-to-set-a-text-fields-maximum-length-visual-studio-style/
Using Interface builder you can link and get the event for "Editing changed" in any of your function.
Now there you can put check for the length
- (IBAction)onValueChange:(id)sender
{
NSString *text = nil;
int MAX_LENGTH = 20;
switch ([sender tag] )
{
case 1:
{
text = myEditField.text;
if (MAX_LENGTH < [text length]) {
myEditField.text = [text substringToIndex:MAX_LENGTH];
}
}
break;
default:
break;
}
}
The following code is similar to sickp's answer but handles correctly copy-paste operations. If you try to paste a text that is longer than the limit, the following code will truncate the text to fit the limit instead of refusing the paste operation completely.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
static const NSUInteger limit = 70; // we limit to 70 characters
NSUInteger allowedLength = limit - [textField.text length] + range.length;
if (string.length > allowedLength) {
if (string.length > 1) {
// get at least the part of the new string that fits
NSString *limitedString = [string substringToIndex:allowedLength];
NSMutableString *newString = [textField.text mutableCopy];
[newString replaceCharactersInRange:range withString:limitedString];
textField.text = newString;
}
return NO;
} else {
return YES;
}
}
To make it work with cut & paste of strings of any length, I would suggest changing the function to something like:
#define MAX_LENGTH 20
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSInteger insertDelta = string.length - range.length;
if (textField.text.length + insertDelta > MAX_LENGTH)
{
return NO; // the new string would be longer than MAX_LENGTH
}
else {
return YES;
}
}
Swift 4
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
guard let text = textField.text else { return true }
let newLength = text.count + string.count - range.length
return newLength <= 10
}
Swift 2.0 +
First of all create a class for this process. Lets call it StringValidator.swift.
Then just paste the following code inside it.
import Foundation
extension String {
func containsCharactersIn(matchCharacters: String) -> Bool {
let characterSet = NSCharacterSet(charactersInString: matchCharacters)
return self.rangeOfCharacterFromSet(characterSet) != nil
}
func containsOnlyCharactersIn(matchCharacters: String) -> Bool {
let disallowedCharacterSet = NSCharacterSet(charactersInString: matchCharacters).invertedSet
return self.rangeOfCharacterFromSet(disallowedCharacterSet) == nil
}
func doesNotContainCharactersIn(matchCharacters: String) -> Bool {
let characterSet = NSCharacterSet(charactersInString: matchCharacters)
return self.rangeOfCharacterFromSet(characterSet) == nil
}
func isNumeric() -> Bool
{
let scanner = NSScanner(string: self)
scanner.locale = NSLocale.currentLocale()
return scanner.scanDecimal(nil) && scanner.atEnd
}
}
Now save the class.....
Usage..
Now goto your viewController.swift class and make your textfield's outlets as..
#IBOutlet weak var contactEntryTxtFld: UITextField! //First textfield
#IBOutlet weak var contactEntryTxtFld2: UITextField! //Second textfield
Now goto the textfield's shouldChangeCharactersInRange method and use like the following.
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
if string.characters.count == 0 {
return true
}
let latestText = textField.text ?? ""
let checkAbleText = (latestText as NSString).stringByReplacingCharactersInRange(range, withString: string)
switch textField {
case contactEntryTxtFld:
return checkAbleText.containsOnlyCharactersIn("0123456789") && prospectiveText.characters.count <= 5
case contactEntryTxtFld2:
return checkAbleText.containsOnlyCharactersIn("0123456789") && prospectiveText.characters.count <= 5
default:
return true
}
}
Don't forget to set the delegate protocol/methods of textfields.
Let me explain about this... I am using the simple extension process of string which I wrote inside an another class. Now I am just calling those extension methods from another class where I need them by adding check and maximum value.
Features...
It will set maximum limit of a particular textfield.
It will set type of accepted keys for particular textfield.
Types...
containsOnlyCharactersIn //Accepts only Characters.
containsCharactersIn //Accepts combination of characters
doesNotContainsCharactersIn //Will not accept characters
Hope this helped....
Thanks..
swift 3.0
This code is working fine when you are paste string more than your character limits.
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
let str = (textView.text + text)
if str.characters.count <= 10 {
return true
}
textView.text = str.substring(to: str.index(str.startIndex, offsetBy: 10))
return false
}
Thanks for your votes. :)
I give a supplementary answer based on #Frouo. I think his answer is the most beautiful way. Becuase it's a common control we can reuse.
private var kAssociationKeyMaxLength: Int = 0
extension UITextField {
#IBInspectable var maxLength: Int {
get {
if let length = objc_getAssociatedObject(self, &kAssociationKeyMaxLength) as? Int {
return length
} else {
return Int.max
}
}
set {
objc_setAssociatedObject(self, &kAssociationKeyMaxLength, newValue, .OBJC_ASSOCIATION_RETAIN)
self.addTarget(self, action: #selector(checkMaxLength), for: .editingChanged)
}
}
func checkMaxLength(textField: UITextField) {
guard !self.isInputMethod(), let prospectiveText = self.text,
prospectiveText.count > maxLength
else {
return
}
let selection = selectedTextRange
let maxCharIndex = prospectiveText.index(prospectiveText.startIndex, offsetBy: maxLength)
text = prospectiveText.substring(to: maxCharIndex)
selectedTextRange = selection
}
//The method is used to cancel the check when use Chinese Pinyin input method.
//Becuase the alphabet also appears in the textfield when inputting, we should cancel the check.
func isInputMethod() -> Bool {
if let positionRange = self.markedTextRange {
if let _ = self.position(from: positionRange.start, offset: 0) {
return true
}
}
return false
}
}
This is the correct way to handle max length on UITextField, it allows the return key to exit the resign the textfield as first responder and lets the user backspace when they reach the limit
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
int MAX_LENGHT = 5;
if([string isEqualToString:#"\n"])
{
[textField resignFirstResponder];
return FALSE;
}
else if(textField.text.length > MAX_LENGHT-1)
{
if([string isEqualToString:#""] && range.length == 1)
{
return TRUE;
}
else
{
return FALSE;
}
}
else
{
return TRUE;
}
}
What about this simple approach. Its working fine for me.
extension UITextField {
func charactersLimit(to:Int) {
if (self.text!.count > to) {
self.deleteBackward()
}
}
}
Then:
someTextField.charactersLimit(to:16)
Other answers do not handle the case where user can paste a long string from clipboard. If I paste a long string it should just be truncated but shown.
Use this in your delegate:
static const NSUInteger maxNoOfCharacters = 5;
-(IBAction)textdidChange:(UITextField * )textField
{
NSString * text = textField.text;
if(text.length > maxNoOfCharacters)
{
text = [text substringWithRange:NSMakeRange(0, maxNoOfCharacters)];
textField.text = text;
}
// use 'text'
}
Got it down to 1 line of code :)
Set your text view's delegate to "self" then add the <UITextViewDelegate> in your .h and the following code in your .m .... you can adjust the number "7" to be whatever you want your MAXIMUM number of characters to be.
-(BOOL)textView:(UITextView *)a shouldChangeTextInRange:(NSRange)b replacementText:(NSString *)c {
return ((a.text.length+c.length<=7)+(c.length<1)+(b.length>=c.length)>0);
}
This code accounts for typing new characters, deleting characters, selecting characters then typing or deleting, selecting characters and cutting, pasting in general, and selecting characters and pasting.
Done!
Alternatively, another cool way to write this code with bit-operations would be
-(BOOL)textView:(UITextView *)a shouldChangeTextInRange:(NSRange)b replacementText:(NSString *)c {
return 0^((a.text.length+c.length<=7)+(c.length<1)+(b.length>=c.length));
}
I have open sourced a UITextField subclass, STATextField, that offers this functionality (and much more) with its maxCharacterLength property.
now how many characters u want just give values
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSUInteger newLength = [textField.text length] + [string length] - range.length;
return (newLength > 25) ? NO : YES;
}
Use this code here RESTRICTED_LENGTH is length you want to restrict for textfield.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if (textField == nameTF) {
int limit = RESTRICTED_LENGTH - 1;
return !([textField.text length]>limit && [string length] > range.length);
}
else
{
return YES;
}
return NO;
}
I did this in Swift for an 8 character limit when using a number pad.
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
return !(textField.text?.characters.count == MAX_LENGTH && string != "")
}
I had to test for string != "" to allow the delete button to work on the number pad, otherwise it wouldn't allow deleting characters in the text field after it reached its max.
For Xamarin:
YourTextField.ShouldChangeCharacters =
delegate(UITextField textField, NSRange range, string replacementString)
{
return (range.Location + replacementString.Length) <= 4; // MaxLength == 4
};
I have implemented a UITextField Extension to add a maxLength property to it.
It's based on Xcode 6 IBInspectables, so you can set the maxLength limit on the Interface builder.
Here is the implementation:
UITextField+MaxLength.h
#import <UIKit/UIKit.h>
#interface UITextField_MaxLength : UITextField<UITextFieldDelegate>
#property (nonatomic)IBInspectable int textMaxLength;
#end
UITextField+MaxLength.m
#import "UITextField+MaxLength.h"
#interface UITextField_MaxLength()
#property (nonatomic, assign) id <UITextFieldDelegate> superDelegate;
#end
#implementation UITextField_MaxLength
- (BOOL)textField:(UITextField *) textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
//validate the length, only if it's set to a non zero value
if (self.textMaxLength>0) {
if(range.length + range.location > textField.text.length)
return NO;
if (textField.text.length+string.length - range.length>self.textMaxLength) {
return NO;
}
}
//if length validation was passed, query the super class to see if the delegate method is implemented there
if (self.superDelegate && [self.superDelegate respondsToSelector:#selector(textField:shouldChangeCharactersInRange:replacementString:)]) {
return [self.superDelegate textField:textField shouldChangeCharactersInRange:range replacementString:string];
}
else{
//if the super class does not implement the delegate method, simply return YES as the length validation was passed
return YES;
}
}
- (void)setDelegate:(id<UITextFieldDelegate>)delegate {
if (delegate == self)
return;
self.superDelegate = delegate;
[super setDelegate:self];
}
//forward all non overriden delegate methods
- (id)forwardingTargetForSelector:(SEL)aSelector {
if ([self.superDelegate respondsToSelector:aSelector])
return self.superDelegate;
return [super forwardingTargetForSelector:aSelector];
}
- (BOOL)respondsToSelector:(SEL)aSelector {
if ([self.superDelegate respondsToSelector:aSelector])
return YES;
return [super respondsToSelector:aSelector];
}
#end