1

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!

rosshump
  • 370
  • 1
  • 4
  • 21

5 Answers5

2

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;
}
Shahab Qureshi
  • 952
  • 1
  • 7
  • 19
  • I have tried this and it works, but limits all textfields to 7 characters. I don't know how to implement it into the my method along with the code I already have (to convert text to uppercase). Could you please elaborate on how this is implemented? Thanks. – rosshump Sep 16 '14 at 09:07
  • Don't need to add it anywhere. Just add Delegate to your TextField. It will be called automatically. Please accept answer so that it should be easy for viewers who are stuck in same kind of problem. – Shahab Qureshi Sep 16 '14 at 09:12
  • Sorry but I don't quite follow you. I have added delegate to the textfield and it does get called automatically, but I also need the code to convert it to uppercase. By adding your code to the method, the characters don't get converted to uppercase anymore. Sorry if I am missing an easy solution! – rosshump Sep 16 '14 at 09:31
  • just do [textField.text uppercaseString] Why you need to add code – Shahab Qureshi Sep 16 '14 at 09:38
  • I need any text a users inputs into this field to automatically convert to uppercase. Unfortunately `[textField.text uppercaseString]` doesn't seem to do this. I will probably look for another solution because it seems using the `textField` `Delegate` method doesn't solve both problems together (convert string to uppercase AND limit to 7 characters). Thanks for your help though. – rosshump Sep 16 '14 at 09:43
  • `[string uppercaseString]` does not work. If you see the code I already have in place uses `stringByReplacingCharactersInRange:range withString:[string uppercaseString]`. I need this code to stay, along with limiting the field to 7 characters. None of these solutions allow me to keep both functionality. – rosshump Sep 16 '14 at 09:49
  • Bro I tried same code it is working fine, delegate is getting called. Characters limit is set to 7. Just add this line [textField setText:[textField.text uppercaseString]]; it will make them uppercase. – Shahab Qureshi Sep 16 '14 at 09:53
  • 1
    Edited answer, try same code. Make sure delegate is attached properly. – Shahab Qureshi Sep 16 '14 at 09:56
  • Strange, I had the same code but when I copied&pasted your code, it worked. Thanks for the help and sorry for the inconvenience. I will accept your answer, thanks again! – rosshump Sep 16 '14 at 10:02
1

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
        }
    }
}
Gabriel.Massana
  • 8,165
  • 6
  • 62
  • 81
0

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;
}
Arvind
  • 450
  • 1
  • 4
  • 11
0

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:

PostcodeField subclass using IB

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.

Community
  • 1
  • 1
rosshump
  • 370
  • 1
  • 4
  • 21
0

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
} 
saksut
  • 673
  • 1
  • 7
  • 14
  • This doesn't prevent the user from tapping the Shift key to turn off capitalization. Also it does not seem to be respected by all 3rd party keyboards like Swiftkey. – stone Aug 08 '15 at 01:34