美文网首页iOS开发那些事
UITextField中限制字符串长度

UITextField中限制字符串长度

作者: _Vitality | 来源:发表于2018-02-02 17:10 被阅读389次

    在日常开发中,针对UITextFiled控件,我们经常需要限制UITextField的长度,避免用户随意输入后传输给服务器,导致字符长度超过数据库对字符长度的限制,导致炸库。
    通常限制字符串长度的方式,是遵从UITextFieldDelegate,实现代理方法:

    - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
        if (self.frameTextField.text.length >= 5) {
            return NO;
        }
        return YES;
    }
    

    这样就可以实现UITextField限制长度功能,但是一般情况下,我们的程序中会包含大量的UITextField,如果每一个都页面实现这样的一段“垃圾代码”,我感觉是很笨拙的方法,而且在同一个页面如果包含多个UITextField的话,我们还需要区分当前的UITextField的字数限制是多少,这样又会产生大量的if...else,switch...case“垃圾代码”。
    在之前的工作中,为了实现这一功能,引用过一些第三方自定义的UITextField,其实也就是继承自UITextField实现的控件,添加了一些属性监听。反正功能是实现了,但是在最近的一次开发,我决定不再使用别人封装好的控件,原因呢,就是我认为是只是为了实现这么小的一个功能,就需要在项目中加入“很多”第三方的文件,而且通常这些第三方实现的功能会比较多,但是这对我并没有什么用,最后决定自己想一下怎么写。

    使用Category

    首先我想到的是用Category来实现这个小功能。排除继承UITextField实现自定义控件的原因很简单,项目中已经创建了大量的UITextField控件了,我实在不想一个一个查找替换,而使用Category就可以很好的避免这种情况,对代码没有侵入性。

    添加一个限制字符串长度的属性

    @property (nonatomic, assign)NSInteger maxLenght;
    由于Category中不能创建实例变量,所以我们需要使用runtime的一些方法,来实现maxLenght的读写。

    - (void)setMaxLenght:(NSInteger)maxLenght {
        objc_setAssociatedObject(self, &kMaxLengthKey, @(maxLenght), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    - (NSInteger)maxLenght {
        NSNumber * number = objc_getAssociatedObject(self, &kMaxLengthKey);
        return  [number integerValue];
    }
    

    上面这段代码我就不解释了,网上太多了。
    至此,我们已经实现了给UITextField限制长度的属性,接下来考虑的是如果让这个属性在运行过程中生效,实现存在的价值。
    在这里想到的了UITextFiled的一个事件:UIControlEventEditingChanged,每次UITextField的输入有变化时,都会触发这个事件。所以我们可以根据这个事件实现一个方法来对字符串的长度进行限制。

    - (void)addLengthObserverEvent {
        [self addTarget:self action:@selector(valueChange) forControlEvents:UIControlEventEditingChanged];
    }
    - (void)valueChange {
        if (self.maxLenght > 0 && self.text.length > self.maxLenght) {
            self.text = [self.text substringToIndex:self.maxLenght];
        }
    }
    

    现在到了最关键的地方,我们如何把addLengthObserverEvent方法注入到UITextField运行过程中?如果我们把这个方法公开public,当我们创建UITextField时,调用注册这个事件,功能就可以实现了,但是我并不认为这是个好的解决方案,用起来麻烦,调用的方法并不是我们需要关心的,我们关心并且只想进行的额外操作就是设置一个maxLength,这也是比较符合设计第三方所需遵守的规则。为了实现这个目的,就该runtime再次登场了。

    + (void)load {
        //使用Xib,StoryBoard创建的UITextField
        Method  method1 = class_getInstanceMethod([self class], @selector(initWithCoder:));
        Method  method2 = class_getInstanceMethod([self class], @selector(AdapterinitWithCoder:));
        
        //使用initWithFrame创建的UITextField
        Method method3 = class_getInstanceMethod([self class], @selector(initWithFrame:));
        Method method4 = class_getInstanceMethod([self class], @selector(AdapterinitWithFrame:));
        method_exchangeImplementations(method1, method2);
        
        method_exchangeImplementations(method3, method4);
    
    }
    
    - (instancetype)AdapterinitWithFrame:(CGRect)frame {
        [self AdapterinitWithFrame:frame];
        if (self) {
            //注册观察UITextField输入变化的方法。
            [self addLengthObserverEvent];
        }
        return self;
    }
    
    
    - (instancetype)AdapterinitWithCoder:(NSCoder *)aDecoder {
        [self AdapterinitWithCoder:aDecoder];
        if (self) {
            [self addLengthObserverEvent];
        }
        return self;
    }
    

    主要用了rutime的两个方法
    Method class_getInstanceMethod(Class cls, SEL name) //获取方法
    method_exchangeImplementations(Method m1, Method m2) //方法交换
    当使用Xib或者StoryBoard创建UITextField时,会调用initWithCoder:这个方法,我们使用AdapterinitWithCoder:来调包程序的运行。在程序运行时,实际上在调用的方法是AdapterinitWithCoder :,这个方法在执行了系统的initWithCoder :方法后,会继续执行我们添加的逻辑代码,实现动态注入的效果。这里有个奇怪的地方[self AdapterinitWithCoder:aDecoder];起初我看到这样的代码时,我也很懵逼,感觉会陷入死循环,其实不会的,因为我们已经对方法进行了交换,[self AdapterinitWithCoder:aDecoder]方法实际上执行的是[self initWithCoder:aDecoder],调用系统的方法,对UITextField进行初始化。之所以放到+(void)load方法中,是因为+ (void)load 会在类或者类的分类添加到 Objective-c runtime 时调用,无需我们手动调用。
    至此,无侵入性实现限制字符串长度的功能就实现,最后不得不赞叹runtime的强大。

    相关文章

      网友评论

        本文标题:UITextField中限制字符串长度

        本文链接:https://www.haomeiwen.com/subject/myrezxtx.html