I have a subclass of UITextField
which sets self.delegate = self
. The subclass is used to prevent special characters being entered into a UITextField
. At first it works fine, but after a few keys are pressed the CPU spikes to 100% and freezes the app. There is no crash log in Xcode because the app never actually crashes, it just stays frozen until I stop it. After some research, I have determined that the problem is setting the delegate
to self
- apparently I should make a separate delegate
for UITextField
? I have searched online but cannot find anything useful on how to do this.
My AcceptedCharacters subclass:
AcceptedCharacters.h
#import <UIKit/UIKit.h>
@interface AcceptedCharacters : UITextField <UITextFieldDelegate>
@end
And
AcceptedCharacters.m
#import "AcceptedCharacters.h"
#define ACCEPTABLE_CHARACTERS @" ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_."
@implementation AcceptedCharacters
- (void)awakeFromNib
{
[super awakeFromNib];
if (self) {
self.delegate = self;
}
}
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
//NSLog(@"AcceptedCharacters");
// Restrict special characters
NSCharacterSet *cs = [[NSCharacterSet characterSetWithCharactersInString:ACCEPTABLE_CHARACTERS] invertedSet];
NSString *filtered = [[string componentsSeparatedByCharactersInSet:cs] componentsJoinedByString:@""];
return [string isEqualToString:filtered];
}
@end
Similar questions I have found on stack overflow:
Application freezes after editing custom UITextField
UITextField delegate jumping to 100% CPU usage and crashing upon using keyboard shortcut
Why does UITextField lock up when setting itself to delegate
I have read through the solutions but they are quite vague for a beginner. I would greatly appreciate someone to amend my code below or even point me in the direction of a tutorial or documentation? Thanks in advance.
EDIT:
I have tested this code on one of my ViewControllers
and it works fine when conforming to the UITextFieldDelegate
protocol and when my textfield delegates
are set to self
(see snippet below).
In my viewDidLoad
method:
self.forenameField.delegate = self;
self.surnameField.delegate = self;
self.addLine1Field.delegate = self;
self.addLine2Field.delegate = self;
self.addLine3Field.delegate = self;
self.addLine4Field.delegate = self;
self.addLine5Field.delegate = self;
self.postcodeField.delegate = self;
self.telLandField.delegate = self;
self.telMobField.delegate = self;
self.emailField.delegate = self;
self.dobField.delegate = self;
self.niNumField.delegate = self;
I would rather create several subclasses of UITextField
and call them on specific textfields rather than have a messy method like the one below.
#define ACCEPTABLE_CHARACTERS @" ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_."
#define NUMBERS_ONLY @"1234567890"
#define CHARACTER_LIMIT 11
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
// Restrict special characters
NSCharacterSet *cs = [[NSCharacterSet characterSetWithCharactersInString:ACCEPTABLE_CHARACTERS] invertedSet];
NSString *filtered = [[string componentsSeparatedByCharactersInSet:cs] componentsJoinedByString:@""];
// Convert postcode field characters to uppercase
if (textField == self.postcodeField) {
[textField setText:[textField.text stringByReplacingCharactersInRange:range withString:[string uppercaseString]]];
return NO;
}
// Set max character limit on tel fields
if (textField == self.telMobField || textField == self.telLandField) {
NSUInteger newLength = [textField.text length] + [string length] - range.length;
NSCharacterSet *cs = [[NSCharacterSet characterSetWithCharactersInString:NUMBERS_ONLY] invertedSet];
NSString *filtered = [[string componentsSeparatedByCharactersInSet:cs] componentsJoinedByString:@""];
return (([string isEqualToString:filtered])&&(newLength <= CHARACTER_LIMIT));
}
//return YES;
return [string isEqualToString:filtered];
}
Try this instead:
@interface MyTextField : UITextField
- (BOOL)stringIsAcceptable:(NSString *)string inRange:(NSRange)range;
@end
@implementation MyTextField
- (BOOL)stringIsAcceptable:(NSString *)string inRange:(NSRange)range {
NSCharacterSet *cs = [[NSCharacterSet characterSetWithCharactersInString:ACCEPTABLE_CHARACTERS] invertedSet];
NSString *filtered = [[string componentsSeparatedByCharactersInSet:cs] componentsJoinedByString:@""];
return [string isEqualToString:filtered];
}
@end
@interface PostalCodeTextField : MyTextField
@end
@implementation PostalCodeTextField
- (BOOL)stringIsAcceptable:(NSString *)string inRange:(NSRange)range {
[self setText:[self.text stringByReplacingCharactersInRange:range withString:[string uppercaseString]]];
return NO;
}
@end
.. more subclasses
Then assign your view controller as the delegate and in your view controller:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
if ([textField isKindOfClass:[MyTextField class]]) {
return [(MyTextField *)textField stringIsAcceptable:string inRange:range];
}
return YES;
}