UITextField多个代理对象

项目中有个需求就是只能输入固定字符的文本框,如:价格文本框。只能输入数字和小数点,如果输入其他字符,则输入无效,即

- (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。

语言表达能力弱鸡,要是有大神能看懂,就请帮忙找找不足什么的吧!

绝对原创,如要转载请注明出处。

原文地址:https://www.cnblogs.com/PhenixWang/p/7112434.html