Set max length of UITextField AND convert to uppercase? - ios

I have a Client Details screen with many UITextField. I need to limit the postcodeField to a maximum of 7 characters and convert to uppercase automatically. I already have code to convert the text to uppercase, but it seems I cannot do anything else with that particular UITextField in its Delegatemethod
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
Here is what I have tried:
#define MAXLENGTH 7
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
if (textField == self.postcodeField) {
self.postcodeField.text = [textField.text stringByReplacingCharactersInRange:range withString:[string uppercaseString]];
return NO;
}
if (self.postcodeField.text.length >= MAXLENGTH && range.length == 0)
{
return NO;
}
return YES;
}
And:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
if (textField == self.postcodeField) {
self.postcodeField.text = [textField.text stringByReplacingCharactersInRange:range withString:[string uppercaseString]];
return NO;
}
NSUInteger newLength = [textField.text length] + [string length] - range.length;
return (newLength > 7) ? NO : YES;
}
This code does not work. I know there are many threads with various solutions to setting a maximum length, but I can't find a solution that caters for uppercase conversion too. I am quite new to iOS so I apologise if this is seen as a duplicate post. Any help is much appreciated!

This will surly help to restrict to 7 Characters.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
[textField setText:[textField.text uppercaseString]];
NSUInteger newLength = [textField.text length] + [string length] - range.length;
return (newLength > 7) ? NO : YES;
}

In my opinion the better approach to your problem is to use NSNotificationCenter with UITextFieldTextDidChangeNotification
Then you can add this code to viewDidLoad:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(maxLength:)
name:UITextFieldTextDidChangeNotification
object:self.postcodeField];
then, you only need to add the selector method, e.g.:
- (void) maxLength: (NSNotification*) notification
{
UITextField *notificationTextField = [notification object];
if (notificationTextField == self.postcodeField)
{
if (self.postcodeField.text.length >= MAXLENGTH)
{
// remove here the extra text
}
}
}

I have run this code and its working fine, so try it out
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if(textField==self.txtPostCodeField)
{
int limit = 100;
return !([textField.text length]>=limit && [string length] >= range.length);
}
return YES;
}

I have decided to implement a new method for achieving what I want. My UITextField delegate method - (BOOL)textField: shouldChangeCharactersInRange: replacementString: was getting very messy as more textfields were added to the view, all of which are doing different things. So I have created a subclass to use on the desired Postcode field. I wasn't able to use the posted solution in a subclass of UITextField as it is bad to set the delegate to self within that subclass (a known issue with UITextField subclassing - see this, this, and this.). The new code is more efficient than the answer I had previously accepted, and can be widely adapted to do many things.
The header file:
PostcodeField.h
#interface PostcodeField : UITextField
- (BOOL)stringIsAcceptable:(NSString *)string inRange:(NSRange)range;
#end
The subclass implementation (another requirement was to only accept specified characters which has been easily implemented):
PostcodeField.m
#define ACCEPTABLE_CHARACTERS #" ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
#define CHARACTER_LIMIT 8
#implementation PostcodeField
- (BOOL)stringIsAcceptable:(NSString *)string inRange:(NSRange)range {
NSUInteger newLength = [self.text length] + [string length] - range.length;
// Check text meets character limit
if (newLength <= CHARACTER_LIMIT) {
// Convert characters to uppercase and return acceptable characters
NSCharacterSet *cs = [[NSCharacterSet characterSetWithCharactersInString:ACCEPTABLE_CHARACTERS] invertedSet];
NSString *filtered = [[string componentsSeparatedByCharactersInSet:cs] componentsJoinedByString:#""];
[self setText:[self.text stringByReplacingCharactersInRange:range withString:[filtered uppercaseString]]];
}
return NO;
}
Then I set the delegate to self on the desired textfield within it's ViewController:
self.postcodeField.delegate = self;
And call it's delegate method on the ViewController:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
// Call PostcodeField subclass on Postcode textfield
if ([textField isKindOfClass:[PostcodeField class]]) {
return [(PostcodeField *)textField stringIsAcceptable:string inRange:range];
}
return YES;
}
And of course, importing the subclass on the ViewController:
#import "PostcodeField.h"
You can set the textfield to use a subclass by navigating to the "Identity Inspector" using IB (Interface Builder) and setting the Custom Class to your subclass:
By using subclasses, your UITextField delegate method can be cleaner and more efficient, and the subclass can be called on as many textfields as you like. If there are multiple textfields on that view, just follow the same process and test for each subclass within the UITextField delegate method. I hope this post will be helpful for anyone wanting to utilise subclasses on UITextFields.

In Swift you can use the code below to do it.
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool
{
if(range.length + range.location > countElements(textField.text))
{
return false
}
textField.autocapitalizationType = UITextAutocapitalizationType.AllCharacters // Capitalize the all characters automatically
var newLength: Int = countElements(textField.text) + countElements(string) - range.length
return (newLength > 7) ? false : true
}

Related

How to detect number length in UITextField?

There is a UITextField with name numbercontent, after entering 8 numbers, it will automatically call the next function. Following is my code
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
if (self.numbercontent.text.length == 7) {
[self.numbercontent resignFirstResponder];
[self stopFly];
}
return YES;
}
But there's a bug
when I enter the 8th number, it will automatically call the next function, but the 8th number isn't shown in the UITextField.
If Change self.numbercontent.text.length == 7 to self.numbercontent.text.length > 7, the 8th number is shown in the UITextField, but I need to enter one more number to call the next function, how to fix this bug, thanks.
Try this, instead of shouldChangeCharactersInRange,
[_txtNum addTarget:self action:#selector(didChangeText:) forControlEvents:UIControlEventEditingChanged];
and then add this method,
-(void)didChangeText:(UITextField*)sender
{
if(sender.text.length==8)
{
[self stopFly];
[self.txtNum resignFirstResponder];
}
}
Although answer suggested by #DhavalBhimani is the standard way to handle this, but alternatives can be used with current approach like:
In Objective-C:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSString *updatedString = [textField.text stringByReplacingCharactersInRange:range withString:string];
if (textField.text.length == 7) {
textField.text = updatedString;
[textField resignFirstResponder];
[self stopFly];
}
return YES;
}
In Swift 4.0:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let updatedString = (textField.text as NSString?)?.replacingCharacters(in: range, with: string)
if textField.text?.count == 7 {
textField.text = updatedString
self.view.endEditing(true)
}
return true
}
Use this.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{
// _textLenghtLimit = your text max lenght
if (_textLenghtLimit > 0) {
if ((range.location >= _textLenghtLimit || textField.text.length + 1 > _textLenghtLimit) && range.length == 0) {
return NO;
}
}
return YES;
}

What is the correct way to change keyboard return button for UITextView

So I have been working on a small requirement that has taken me way more time than I would like, as small requirements in UIKit seem to do sometimes:
When a user enters a password longer than 3 characters you change the
keyboard to have a done button.
Simple enough... it seems that KVO isn't fired until editing ends, and neither is the textFieldDidEndEditing: delegate method called. Ok, easy enough, just do our logic in the -(BOOL)textField:shouldChangeCharactersInRange:replacementString: delegate callback...
Attempt A:
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSString * newString = [textField.text stringByReplacingCharactersInRange:range withString:string];
if (newString.length >=3)
{
textField.returnKeyType = UIReturnKeyGo;
}
return YES;
}
Attempt A, does nothing... never changes the keyboard to Go
Attempt B:
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSString * newString = [textField.text stringByReplacingCharactersInRange:range withString:string];
if (newString.length >=3)
{
textField.returnKeyType = UIReturnKeyGo;
[textField resignFirstResponder];
[textField becomeFirstResponder];
}
return YES;
}
Attempt B: yay, keyboard button changes, but when we resignFirstResponder, we discard the new input so the User can't enter their password... bad
Attempt C:
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSString * newString = [textField.text stringByReplacingCharactersInRange:range withString:string];
if (newString.length >=3)
{
textField.returnKeyType = UIReturnKeyGo;
}else{
textField.returnKeyType = UIReturnKeyDefault;
}
textField.text = newString;
[textField resignFirstResponder];
[textField becomeFirstResponder];
return NO;
}
Attempt C is tricky, we return NO, telling the delegate not to accept the edit, but that is ok, because we explicitly set the string in the delegate (this seems like sort of a bad idea), but it all works, except that when you resign firstResponder status it changes your keyboard (if you had the number keyboard up, it will switch to default after every keystroke)
Attempt D:
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSString * newString = [textField.text stringByReplacingCharactersInRange:range withString:string];
if (newString.length >=3)
{
textField.returnKeyType = UIReturnKeyGo;
}else{
textField.returnKeyType = UIReturnKeyDefault;
}
//detect if we have crossed a boundry
if ((textField.text.length >=3) != (newString.length >=3))
{
[textField resignFirstResponder];
[textField becomeFirstResponder];
}
textField.text = newString;
return NO;
}
Attempt D is pretty good, it will only resign first responder as you cross the 2/3 or 3/2 edge so you only lose your keyboard once, not a big deal usually
so the question, what is the best practice way to do this?
(resigning first responder only seems to cancel the edit if using secure input, if you aren't familiar with this problem), I have also prepared a sample project, as to help anyone that wants to look at it: sample project
That's far too much work. Just make yourself the delegate of the UITextView and you will get TextViewDidChange messages.
- (void)textViewDidChange:(UITextView *)textView;
Or if using a UITextField register for its UITextFieldTextDidChangeNotification message.
add the delegate on text did change
[textField addTarget:self action:#selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
-(void)textFieldDidChange :(UITextField *)theTextField
{
enter your logic here
}
Try this
textField.returnKeyType = UIReturnKeyNext;

UITextField check for input

I have in my TableViewCell 2 TextFields.
For the 2nd TextField i need a confirm to enable a button.
I´m using this code to determine if the confirm button should be enabled, but it doesn't work:
-(BOOL)textField:(UITextField*)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
textField = self.installDeviceCell.passWordTextFieldOutlet;
[textField addTarget:selfaction:#selector(checkTextField:)forControlEvents:UIControlEventEditingChanged];
return YES;
}
-(void)checkTextField:(id)sender
{
UITextField* textField = (UITextField*)sender;
if ([textField.text length] < 7) {
self.installDeviceCell.installDeviceTouchUpInsideButton.enabled = NO;
}else if ([textField.text length] > 7){
self.installDeviceCell.installDeviceTouchUpInsideButton.enabled = YES;
}else{
self.installDeviceCell.installDeviceTouchUpInsideButton.enabled = YES;
}
}
Have you assigned your UITextFieldDelegate for your UITextField when it is being instatiated? If don't assign the it the delegate methods for the UITextField will not be called.
Also make sure that in the header file of the delegate you specify the delegate of the interface e.g.
#interface MyDelegate <UITextFieldDelegate>
// ...
#end
Finally you're doing your text calculations a little oddly. You can do all your computations witin shouldChangeCharactersInRange. You also don't need to add the target or overwrite the textField property. Simply check if the replacementString is greater than 7 characters. Your final code might look something like this:
-(BOOL)textField:(UITextField*)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if ([string length] < 7) {
self.installDeviceCell.installDeviceTouchUpInsideButton.enabled = NO;
} else {
self.installDeviceCell.installDeviceTouchUpInsideButton.enabled = YES;
}
return YES;
}
See this great answer on delegation for more info: https://stackoverflow.com/a/626946/740474

Moving focus from UITextfield after entering 2 characters

The next code will work only when the user will enter the third character. I want the focus to move TextField when the user enters the second character but still keep the first 2 characters in the first TextField. if I'll try something likenewString.length < 2 , when entering 2 characters in a row I'll get the first character in the first UITextField and the second character in the second UITextField.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSString *newString = [textField.text stringByReplacingCharactersInRange:range withString:string];
if (newString.length <= 2 )
{
return YES;
}
else
{
NSInteger nextTag;
nextTag = textField.tag + 1;
UIResponder* nextResponder = [textField.superview viewWithTag:nextTag];
if (nextResponder) {
[nextResponder becomeFirstResponder];
}
else {
[textField resignFirstResponder];
}
}
return YES;
}
I belive this should work:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSString *newString = [textField.text stringByReplacingCharactersInRange:range withString:string];
if(newString.length == 2)
{
UIResponder* nextResponder = [textField.superview viewWithTag:textField.tag + 1];
if (nextResponder) {
[nextResponder performSelector:#selector(becomeFirstResponder) withObject:nil afterDelay:0.1];
}
else {
[textField resignFirstResponder];
}
}
return newString.length <= 2;
}
Please try it and tell me if it works. If not please tell me a bit more about your setup and I will try to set up a view like yours and it will be easier for me to try solutions.
Ok, EDIT The above works. It is not very beautiful but it does the job. It is sad that apple would not add a method for didChangeCharactersInRange which would be much more appropriate for this.
EDIT Edited according to Sha's findings 0.1 instead of 0 in the timer interval
EDIT Changed according to Mike's suggestion

Enabling button if I write in TextField

I am having some problems with enabling a button when you write something in a TextField.
I got it to disable if a there's nothing in the TextField but I can't make it to enable again when you write something in it.
I have something like this:
- (void)viewDidLoad {
[super viewDidLoad];
NSUInteger textLength = [_Name.text length];
[_doneButton setEnabled:(textLength > 0)];
}
Set delegate to the UITextField and create a method for text change in ViewDidLoad as follows.
[self.textFieldTemp addTarget:self action:#selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
And add the method as follows.
- (void)textFieldDidChange:(UITextField *)textField {
if(textField == self.textFieldTemp)
self.buttonTemp.enabled = ([self.textFieldTemp.text length] > 0) ? YES : NO; }
Try this
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
if([textField.text length]>0){
[doneButton setEnabled:YES];
}
else{
[doneButton setEnabled:NO ;
}
Hope this May be help you..
Use UITextField delegate method to enable your button again. When your textField characters are changed, delegate method will be called, enable your button here.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
_doneButton.enabled = YES;
}
Put the lines in textfield delegate method:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
It'll work then. This method is called every time you input a character in textfield. But ViewDidLoad is called only when the viewcontroller is loaded.
Hope it will work for you.
These two functions will enable and disable your button according the text in your textfield
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
_doneButton.enabled = YES;
}
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField{
if([textField.text length]>0){
[doneButton setEnabled:YES];
}
else{
[doneButton setEnabled:NO ;
}
You can use delegates of UITextField to enable and disable the button like this.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
{
if (textField.text.length!=0)
{
yourButton.enabled = YES;
}
else
{
yourButton.enabled = NO;
}
}
The method I used didn't use the delegates at all. I created this method in the controller class:
- (IBAction)textFieldChanged:(id)sender
{
self.doneButton.enabled = ([self.Name.text length] > 0) ? YES : NO;
}
Then, in the Interface Builder, I set the text field "Editing Changed" action to target the new method. Then, similar to #Paramasivan Samuttiram, I added an extra call to it in the viewDidLoad:.
[self textFieldChanged:self.Name];
That part allows the button to be in the correct state initially.

Resources