【问题标题】:How properly subclass UITextField in iOS?如何在 iOS 中正确继承 UITextField?
【发布时间】:2014-09-23 15:09:28
【问题描述】:

我知道互联网上有很多关于这个问题的信息,但我被困住了。我有一个应用程序,我需要在其中自定义我的UITextFields。例如,我需要在编辑开始时更改边框颜色,或者我需要限制我可以在不同的文本字段中设置多少个字符。我还需要几个文本字段中的图像,必须在左侧缩进。为此,我决定制作自定义 UITextField 类(子类 UITextField)。我从UITextField(实现<UITextFieldDelegate>)创建了新类。在那个类中,我使用self.delegate = self(这在互联网上被广泛使用,人们说它正在工作)所以我可以在我的自定义类中实现shouldChangeCharactersInRangetextFieldShouldBeginEditing。我的问题是,在此配置中,我收到无限循环并重新启动应用程序(请参阅my question 关于此)。这来自self.delegate = self。我知道在某些情况下我可以使用observer,但在那种情况下,我如何在我的课堂上实现shouldChangeCharactersInRange? 如果我不以这种方式实现我的类并将我的文本字段委托给我的视图控制器。我必须在我的视图控制器类中实现所有这些方法,在我看来这是非常丑陋的解决方案。

所以我的问题是如何正确实现 UITextField 子类?

附:我想我做错了,但我不知道哪个是正确的。

编辑:

这是我的代码:

MyCustomTextField.h

@interface MyCustomTextField : UITextField 

@property (nonatomic) int maxSymbols;
@property (nonatomic) int leftIndent;

@end

MyCustomTextField.m

@interface MyCustomTextField () <UITextFieldDelegate>

@end

@implementation MyCustomTextField
- (id)initWithCoder:(NSCoder *)aDecoder{
    if (self = [super initWithCoder:aDecoder]) {

        self.delegate = self;

        self.clipsToBounds = YES;
        [self setLeftViewMode:UITextFieldViewModeAlways];

        UIImageView *imageView1 = [[UIImageView alloc]
                                   initWithFrame:CGRectMake(0, 0, 37, 20)];
        imageView1.image = [UIImage imageNamed:@"otp_back"];
        self.leftView = imageView1;

    }
    return self;
}

- (CGRect) leftViewRectForBounds:(CGRect)bounds {

    CGRect textRect = [super leftViewRectForBounds:bounds];
    textRect.origin.x = 5;
    return textRect;
}

还有这个检查最大长度的方法:

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{

    // Restrict number of symbols in text field to "maxSymbols"
    NSUInteger oldLength = [textField.text length];
    NSUInteger replacementLength = [string length];
    NSUInteger rangeLength = range.length;

    NSUInteger newLength = oldLength - rangeLength + replacementLength;

    BOOL returnKey = [string rangeOfString: @"\n"].location != NSNotFound;

    return newLength <= (int)_maxSymbols || returnKey;
} 

当我转到文本字段并开始从模拟器的虚拟键盘键入时,我收到无限循环并以 BAD ACCESS 退出。这很奇怪,因为如果键盘是数字或密码类型,或者我从 Mac 键盘输入,我没有收到问题。

【问题讨论】:

  • 你能展示一些实际的代码吗?很大程度上取决于你什么时候说什么。

标签: ios objective-c uitextfield


【解决方案1】:

我不同意一直在视图控制器中设置委托。使用 UITextField 本身作为委托有几个明显的优势,例如当许多不同的视图控制器共享相同的文本字段行为时,代码更简洁。

在 iOS 8 中,自我委托引用循环似乎是“固定的”。但是对于那些针对 iOS 7 及更低版本的用户,我们使用了代理对象来绕过循环:

InputTextProxy.h:

@interface InputTextProxy : NSObject <UITextFieldDelegate>
@property (weak, nonatomic) id<UITextFieldDelegate> proxiedDelegate;
@end

InputTextProxy.m:

#import "InputTextProxy.h"

@implementation InputTextProxy

- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
    return [self.proxiedDelegate textFieldShouldReturn:textField];
}

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
    return [self.proxiedDelegate textField:textField shouldChangeCharactersInRange:range replacementString:string];
}

@end

你可以根据需要添加更多的回调,我们只使用以上两个。

现在在您的子类 MyCustomTextField.m 中创建一个属性:

@property (strong, nonatomic) InputTextProxy *inputProxy;

- (InputTextProxy *)inputProxy
{
    if (!_inputProxy)
        _inputProxy = [[InputTextProxy alloc] init];
    return _inputProxy;
}

使用它来设置你的委托:

self.delegate = self.inputProxy;
self.inputProxy.proxiedDelegate = self;

【讨论】:

  • 我同意你的看法,@e_x_p,但这是唯一可行的解​​决方案(循环有时会发生)。在某些情况下,您需要在edit 事件中管理ViewController 中的其他内容。我一定会试试你的解决方案。
  • 我无法忍受为 15 个以上的视图控制器编写相同的代码......希望它对你有帮助 @new2ios
  • 因为这是唯一的解决方案,我已经编写了 VC,@e_x_p 2。我将尝试在我的下一个项目中使用你的方式,或者当我有时间将我的应用程序移动到 swift 时。
【解决方案2】:

我不会在您的自定义 UITextField 中将委托设置为 self,它不是很整洁。 我要做的只是拥有一个属性或一个编辑方法,当您收到委托调用时,您可以在控制器中调用它们。

基本上在您的视图控制器中:

- (void)viewDidLoad{
    ....
    self.textField = [[CustomTextField alloc] init];
    self.textField.delegate = self;

}

- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField{
     self.textField.editMode = YES;
}

在您的 CustomTextField 中

-(void)setEditMode:(BOOL)edit{
    if(edit){
        self.layer.borderColor=[[UIColor redColor]CGColor];
        self.layer.borderWidth= 1.0f;
    }
}

【讨论】:

    【解决方案3】:

    对于编辑开始和结束,您有becomeFirstResponderresignFirstResponder。这是最好的地方。覆盖并且不要忘记调用超级。对于输入操作,您必须检查文档。有两种方式,通过通知和子类。两者都提供不同程度的灵活性。

    【讨论】:

    • 我是 iOS 开发的新手,您能否提供一个链接,其中包含您的解决方案示例?我知道,当我进行编辑时,我会更改 FirstResponder,从那时起您的解决方案听起来不错,但我不知道该怎么做。
    • @new2ios 我没有特定的链接,因为它非常基础并且包含在文档中,但是您可以查看它在 FlatUIKit 中是如何完成的:github.com/Grouper/FlatUIKit/blob/master/Classes/ios/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-03-15
    • 1970-01-01
    • 2010-11-17
    • 2011-05-03
    • 2011-04-19
    • 1970-01-01
    • 2014-10-11
    相关资源
    最近更新 更多