Intercept Objective-C delegate messages within a subclass

前端 未结 4 694
渐次进展
渐次进展 2020-12-04 08:13

I have a subclass of UIScrollView in which I need to internally respond to scrolling behaviour. However, the viewcontroller will still need to listen to scrolling delegate c

4条回答
  •  一生所求
    2020-12-04 08:25

    To avoid overriding all of the delegate methods manually, you can use message forwarding. I just implemented the same thing using an intermediate proxy class as follows:

    MessageInterceptor.h

    @interface MessageInterceptor : NSObject {
        id receiver;
        id middleMan;
    }
    @property (nonatomic, assign) id receiver;
    @property (nonatomic, assign) id middleMan;
    @end
    

    MessageInterceptor.m

    @implementation MessageInterceptor
    @synthesize receiver;
    @synthesize middleMan;
    
    - (id)forwardingTargetForSelector:(SEL)aSelector {
        if ([middleMan respondsToSelector:aSelector]) { return middleMan; }
        if ([receiver respondsToSelector:aSelector]) { return receiver; }
        return [super forwardingTargetForSelector:aSelector];
    }
    
    - (BOOL)respondsToSelector:(SEL)aSelector {
        if ([middleMan respondsToSelector:aSelector]) { return YES; }
        if ([receiver respondsToSelector:aSelector]) { return YES; }
        return [super respondsToSelector:aSelector];
    }
    
    @end
    

    MyScrollView.h

    #import "MessageInterceptor.h"
    
    @interface MyScrollView : UIScrollView {
        MessageInterceptor * delegate_interceptor;
        //...
    }
    
    //...
    
    @end
    

    MyScrollView.m (Edited, with thanks to jhabbott):

    @implementation MyScrollView
    
    - (id)delegate { return delegate_interceptor.receiver; }
    
    - (void)setDelegate:(id)newDelegate {
        [super setDelegate:nil];
        [delegate_interceptor setReceiver:newDelegate];
        [super setDelegate:(id)delegate_interceptor];
    }
    
    - (id)init* {
        //...
        delegate_interceptor = [[MessageInterceptor alloc] init];
        [delegate_interceptor setMiddleMan:self];
        [super setDelegate:(id)delegate_interceptor];
        //...
    }
    
    - (void)dealloc {
        //...
        [delegate_interceptor release];
        //...
    }
    
    // delegate method override:
    - (void)scrollViewDidScroll:(UIScrollView *)scrollView {
        // 1. your custom code goes here
        // 2. forward to the delegate as usual
        if ([self.delegate respondsToSelector:@selector(scrollViewDidScroll:)]) {
            [self.delegate scrollViewDidScroll:scrollView];
        }
    }
    
    @end
    

    With this approach, the MessageInterceptor object will automatically forward all delegate messages to the regular delegate object, except for the ones that you override in your custom subclass.

提交回复
热议问题