A UITexField is a native iOS object that allows a user to input text with a keyboard. However, in some cases what you really want to do is to allow only a discrete number of text inputs.
The first thing you may try is to bring up a UIDatePicker or a UIPicker instead of the normal keyboard when the user taps on a text field. Then, you could try changing the UITextField‘s value based on what the user selects on one of these pickers.
This is accomplished by rewriting a UITextField‘s inputView property.
//Create a UIDatePicker UIDatePicker datePicker = [[UIDatePicker alloc] init]; datePicker.datePickerMode = UIDatePickerModeDate; [datePicker addTarget:self action:@selector(datePickerChanged:) forControlEvents:UIControlEventValueChanged]; //Set the textField's inputView to be the UIPicker self.textField.inputView = datePicker;
But then you run into this problem:
The cursor is still there. This may seem like a small problem but it is really, really bad. The user could use the cursor to copy-paste whatever he or she wants in there. This defeats the purpose of limiting possible inputs and can even corrupt your data if you are sending this input to a backend server.
Then there is the UI design problem. The user is confused. Do you want me to type or do you want me to choose? Keeping the cursor there is sloppy.
There may be other ways around this problem, including hiding the UITextField behind another UITextField, using invisible buttons, disabling copy-paste functionality, etc… These all feel like they are cool little hacks (I couldn’t get any of them to work) but the way I’m proposing here is simple, elegant and object-oriented. It’s a UILabel subclass that I’m calling PRLabel until I can find a better name for it.
Header file:
#import <UIKit/UIKit.h> @interface PRLabel : UILabel @property (strong, nonatomic, readwrite) UIView* inputView; @property (strong, nonatomic, readwrite) UIView* inputAccessoryView; @end
Implementation file:
#import "PRLabel.h"
@implementation PRLabel
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
- (BOOL)isUserInteractionEnabled
{
return YES;
}
- (BOOL)canBecomeFirstResponder
{
return YES;
}
# pragma mark - UIResponder overrides
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[self becomeFirstResponder];
}
@end
PRLabel overrides five things from the original UILabel. The first two are the properties inputView and inputViewAccessory. These properties are inherited from the UIResponder and they determine what views (keyboard, date picker, etc) come up when a UIResponder object becomes firstResponder.
Both of these properties are read-only by default in a UILabel so we have to override their pointers in the implementation file (e.g. @property (strong, nonatomic, readwrite) UIView* inputView).
Third, you need to override the getter method isUserInteractionEnabled to always return YES. This property determines whether or not a UI object should respond to touch events. The default is NO for the UILabel class because static text is not supposed to be interactive.
Fourth, UILabel cannot become firstResponder by default. The inputView only comes up when an object becomes first responder so we also need to override the method canBecomeFirstResponder in our UILabel subclass so that it always returns YES.
Finally, we need to override the touchesEnded method that is inherited from UIResponder so that the PRLabel object becomes first responder by default when it detects a touch. This can also be done with a gesture recognizer, but doing it this way maintains a clean UIResponder chain.
After all these changes have been made, you can use your new PRLabel instead of a UITextField and you won’t have to worry about the cursor when you hook it up to a picker.
