I have a subclass of UIScrollView that I need to internally respond to scrolling behaviour. However the viewcontroller will still need to listen to scrolling delegate callbacks so I can't outright steal the delegate within my component. Is there a way to keep the property named "delegate" and just listen to messages sent along it, or else somehow internally hijack the delegate property and forward messages outward after running some code?
+3
A:
Yes, but you'll have to override every delegate method in the docs. Basically, make a second delegate property and implement the delegate protocol. When your delegate methods are called, take care of your business and then call the same method on your second delegate from the delegate method that was just run. E.g.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
// Do stuff here
[self.delegate2 scrollViewDidScroll:scrollView];
}
Matt Williamson
2010-08-16 23:23:56
Uglyyyyyyyyy, but unfortunately necessary :/
tc.
2010-08-17 03:03:40
Yeah, the only other alternative is to use nsnotifications
Matt Williamson
2010-08-17 05:30:56
A:
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
@implementation MyScrollView
- (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 ([delegate_interceptor.receiver
respondsToSelector:@selector(scrollViewDidScroll:)]) {
[delegate_interceptor.receiver] 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.
e.James
2010-10-05 10:00:55
Whoops: discovered a small bug: The intercepted delegate method needs to check that the real delegate will respond before passing on the message. Fixed.
e.James
2010-10-07 08:12:39