6
votes

I'm writting a subclass of UITableView and I want my subclass to handle some of the UITableViewDelegate methods itself before passing them along to the "real" delegate as well as forward all the UITableViewDelegate methods not implemented by my subclass.

In the subclass I have a private property:

@property (nonatomic, assign) id <UITableViewDelegate> trueDelegate;

which holds the "real delegate" that all the unimplemented methods should forward to. In both my init methods I set

self.delegate = self;

and I override - (void)setDelegate:(id) like this

-(void)setDelegate:(id<UITableViewDelegate>)delegate {
    if (delegate != self) {
        _trueDelegate = delegate;
    } else {
        [super setDelegate:self];
    }
}

Then I override these to handle the message forwarding

-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    NSMethodSignature *sig;
    sig = [[self.delegate class] instanceMethodSignatureForSelector:aSelector];
    if (sig == nil) {
        sig = [NSMethodSignature signatureWithObjCTypes:"@^v^c"];
    }
    return sig;
}

- (void)forwardInvocation:(NSInvocation *)anInvocation {
    SEL selector = anInvocation.selector;
    if ([self respondsToSelector:selector]) {
        [anInvocation invokeWithTarget:self];
    } else {
        [anInvocation invokeWithTarget:_trueDelegate];
    }
}

The problem is that the unimplemented delegate methods never get called on the tableview, therefore they are not given a chance to be forwarded along to the _trueDelegate object.

I tried checking for them here:

- (BOOL)respondsToSelector:(SEL)aSelector {

}

but that method is never called for the UITableViewDelegate methods although it catches other methods just fine.

1
Did you declare your extended class as conforming to the delegate protocols? - Chris Trahey
I ask 'cause it's possible that the tableView uses conformsToProtocol: instead of respondsToSelector: when calling delegate methods. - Chris Trahey
Indeed I did, that very well could be the problem, I'll check that out - Lance

1 Answers

12
votes

For performance, UITableView checks and remembers which delegate methods are available as soon as the delegate is set. You set the delegate self first, then the trueDelegate. So at the time the delegate is set on the UITableView, trueDelegate is nil, and so -respondsToSelector: on that one always returns NO.

To fix that, set the delegate after trueDelegate is set. Also, you can simplify the forwarding code. Remove all the code you have above except for the property and replace it with:

- (void)setDelegate:(id <UITableViewDelegate>)delegate
{
    if (delegate == self) return;

    self.trueDelegate = delegate;
    [super setDelegate:self];
}

- (BOOL)respondsToSelector:(SEL)aSelector
{
    if ([super respondsToSelector:aSelector]) return YES;

    return [self.trueDelegate respondsToSelector:aSelector];
}

- (id)forwardingTargetForSelector:(SEL)aSelector
{
    return self.trueDelegate;
}