项目中有个需求就是只能输入固定字符的文本框,如:价格文本框。只能输入数字和小数点,如果输入其他字符,则输入无效,即
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { return NO; }
通常情况下会将字符判断的逻辑放在这个代理方法中判断,如果符合就返回YES ,如果不符合就返回NO。但是就算将判断的逻辑提炼出来,在VC的中还是要实现代理方法。或者自定义一个textField,使用一个通知UITextFieldTextDidChangeNotification来做这个逻辑判断。
今天我提供的是另一个解决的思路,就是想办法让textField可以同时响应两个对象中的代理方法。即将CustomTextField必须要实现的代理直接放在一个Delegate类中,使用CustomTextField的人则不用关心代理的逻辑,而只实现他需要关注的代理就可以了。
demo地址:
https://github.com/DawnWdf/DWDoubleDelegateDemo.git
大体思路如下:(VCD代表在viewcontroller里面设置textfield的代理,MD代表自定义代理,TF代表textField)
1:设置TF代理为MD,MD配置属性originalDelegate为VCD
2:获取TF所有代理及应该实现的代理方法
3:如果VCD实现但是MD没有实现则进行实现方法的重新定向
上代码吧,语言表达实在不行。
@interface MyCustomTextField() @property (nonatomic, strong) MyTextFieldDelegate *currentDelegate; @end @implementation MyCustomTextField - (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { MyTextFieldDelegate *del = [[MyTextFieldDelegate alloc] init];; del.textField = self; self.currentDelegate = del; self.delegate = (id)self.currentDelegate; } return self; } - (void)setDelegate:(id)delegate{ if ([delegate isMemberOfClass:[MyTextFieldDelegate class]]) { [super setDelegate:delegate]; return; } self.currentDelegate.originDelegate = delegate; } @end
设置不同的代理,欺骗一下使用者。
- (void)setOriginDelegate:(id)originDelegate { _originDelegate = originDelegate; [self dw_allProtocolMethodImplementation]; } - (void)dw_allProtocolMethodImplementation{ //协议需要实现的方法 unsigned int protocolCount = 0; //获取当前类所有的协议 __unsafe_unretained Protocol **allP = class_copyProtocolList(self.class, &protocolCount); for (int i = 0; i < protocolCount; i++) { Protocol *protocol = allP[i]; BOOL conform = class_conformsToProtocol(self.class, protocol); if (conform) { unsigned int protocolMethodCount = 0; //获取协议方法描述 struct objc_method_description *protocolDes = protocol_copyMethodDescriptionList(protocol, NO, YES, &protocolMethodCount); for (int i = 0; i < protocolMethodCount; i++) { struct objc_method_description protocolObject = protocolDes[i]; SEL selector = protocolObject.name; //当前类是否实现此方法 BOOL isResponse = class_respondsToSelector(self.class, selector); //originalDelegate是否实现此方法 BOOL isOriginalResponse = class_respondsToSelector([self.originDelegate class], selector); if ((!isResponse) && isOriginalResponse) { //如果当前类没有实现但是originalDelegate实现了 则替换 Method originalMethod = class_getInstanceMethod([self.originDelegate class], selector); class_replaceMethod(self.class, selector, class_getMethodImplementation([self.originDelegate class], selector), method_getTypeEncoding(originalMethod)); } } } } }
runtime拿一下代理方法,并且实现方法替换一下
这样在MD和VCD中的代理则都会执行了。
这个思路用来解决textfield的问题没有明显优势,毕竟textfield的代理少,并且还有通知的方法可以解决。
但是如果用来解决像uitableview的封装问题,优势就比较明显了。比如在cell类型比较多的情况下,如果使用代理里面做判断的方式,那么每一个代理方法里面都会有很多if else的判断,相对较好的办法是将常用的代理按照cell的种类进行一个封装。这样一种cell的所有处理,包括cell的ID cell对应的xib cell的高度等都在一个block或者代码段中执行,可读性会比较高。但是坏处就是全部的代理方法的实现都要重定向,相当麻烦,这个blog的思路就是用来解决这个问题的。如果以后不开心了,就写个这样的demo。
语言表达能力弱鸡,要是有大神能看懂,就请帮忙找找不足什么的吧!
绝对原创,如要转载请注明出处。