I want make a UITextField subclass that has a predefined action on return key presses, but is in all other respects no different from UITextField. Edit: I'd prefer the delegate to wo开发者_开发知识库rk as usual--one way to do this is to create a delegate that sends textFieldshouldReturn:
to the subclass and other messages to an actual delegate, but I would prefer to keep it to one object if possible.
It's super easy and here is how:
In your UITextField
subclass add the target action method for UIControlEventEditingDidEndOnExit
event
[self addTarget:self action:@selector(returnPressed) forControlEvents:UIControlEventEditingDidEndOnExit];
Now the selector will be called every time return key is pressed.
The solution is to have your UITextField subclass have a custom private delegate that you would insert between your textfield and its "normal" delegate.
You would create your private delegate in your initWithFrame: and initWithCoder: methods, and use it as your delegate. A strong/retain property is needed, since the delegate property of UITextField does not retain its content.
@interface MyTextFieldPrivateDelegate: NSObject<UITextFieldDelegate>
@end
@interface MyTextField()
@property (nonatomic, strong) MyTextFieldPrivateDelegate *privateDelegate;
@end
@implementation MyTextField
@synthesize privateDelegate;
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.privateDelegate = [[MyTextFieldPrivateDelegate alloc] init];
self.delegate = self.privateDelegate;
}
return self;
}
You would override setDelegate: in order to insert your private delegate in between.
@interface MyTextFieldPrivateDelegate: NSObject<UITextFieldDelegate>
@property (nonatomic, weak) id<UITextFieldDelegate> delegate;
@end
@implementation MyTextFieldPrivateDelegate
@synthesize delegate;
@end
@implementation MyTextField
- (void)setDelegate:(id<UITextFieldDelegate>)delegate
{
if (delegate == self.privateDelegate) {
[super setDelegate:delegate];
} else {
self.privateDelegate.delegate = delegate;
}
}
@end
Your private delegate would implement your custom textFieldShouldReturn: implementation, and all other UITextFieldDelegate methods
@implementation MyTextFieldPrivateDelegate
- (BOOL)textFieldShouldReturn:(UITextField*)textField {
// ...
}
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
if ([self.delegate respondsToSelector:@selector(textFieldShouldBeginEditing:)]) {
return [self.delegate textFieldShouldBeginEditing:textField];
}
return YES;
}
// etc.
@end
I think Gwendal was on the right track, so I took her answer and expanded it. All you have to do is include the code below in your project, and then, in Interface Builder, change the class from UITextField to ARTextField (AR = Automatic Resigner) to get the desired behavior.
I'm still trying to decide if the userDelegate property declaration should be strong or weak...
ARTextField.h File:
//
// ARTextField.h
//
// Created by Terry Grossman on 3/21/14.
//
#import <UIKit/UIKit.h>
@interface ARTextField : UITextField <UITextFieldDelegate>
@property (nonatomic, strong) NSObject<UITextFieldDelegate> *userDelegate;
@end
ARTextField.m File:
//
// ARTextField.m
//
// Created by Terry Grossman on 3/21/14.
//
#import "ARTextField.h"
@implementation ARTextField
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
[super setDelegate:self];
}
return self;
}
-(id) initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self)
{
[super setDelegate:self];
}
return self;
}
-(void) awakeFromNib
{
[super awakeFromNib];
[super setDelegate:self];
}
// if user wants a delegate, we stash it.
- (void)setDelegate:(id<UITextFieldDelegate>)delegate
{
if (delegate != self)
{
self.userDelegate = delegate;
}
}
- (BOOL)textFieldShouldReturn:(UITextField*)textField
{
[textField resignFirstResponder];
if (self.userDelegate != nil && [self.userDelegate respondsToSelector:@selector(textFieldShouldReturn:)])
{
return [self.userDelegate textFieldShouldReturn:textField];
}
return NO;
}
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
if (self.userDelegate != nil && [self.userDelegate respondsToSelector:@selector(textFieldShouldBeginEditing:)]) {
return [self.delegate textFieldShouldBeginEditing:textField];
}
return YES;
}
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if (self.userDelegate != nil && [self.userDelegate respondsToSelector:@selector(textField:shouldChangeCharactersInRange:replacementString:)])
{
return [self.userDelegate textField:textField shouldChangeCharactersInRange:range replacementString:string];
}
return YES;
}
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField
{
if (self.userDelegate != nil && [self.userDelegate respondsToSelector:@selector(textFieldShouldEndEditing:)]) {
return [self.userDelegate textFieldShouldEndEditing:textField];
}
return YES;
}
- (BOOL)textFieldShouldClear:(UITextField *)textField
{
if (self.userDelegate != nil && [self.userDelegate respondsToSelector:@selector(textFieldShouldClear:)]) {
return [self.userDelegate textFieldShouldClear:textField];
}
return YES;
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
if (self.userDelegate != nil && [self.userDelegate respondsToSelector:@selector(textFieldDidEndEditing:)]) {
[self.userDelegate textFieldDidEndEditing:textField];
}
}
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
if (self.userDelegate != nil && [self.userDelegate respondsToSelector:@selector(textFieldDidBeginEditing:)]) {
[self.userDelegate textFieldDidBeginEditing:textField];
}
}
@end
You can override textFieldShouldReturn method in your subclass. And that class will do all the things a normal UItextField does.
UPDATE
@interface MyTextField : UITextField {
}
@end
@implementation MyTextField
- (CGRect)textRectForBounds:(CGRect)bounds {
return CGRectInset(bounds, 10, 0);
}
- (CGRect)editingRectForBounds:(CGRect)bounds {
return CGRectInset(bounds, 10, 0);
}
- (void) drawRect:(CGRect)rect{
[super drawRect:rect];
}
-(BOOL)textFieldShouldReturn:(UITextField*)textField {
//write you code here to capture the return key.
//Make sure to change textfield's class from interface builder if you are using interfce builder or initialize by code.
}
@end
精彩评论