基于 H2CO3 的建议,即保存一个指向真正代表的隐藏指针并将所有传入消息转发给它,我提出了以下解决方案。
声明一个私有委托变量来存储对传递给setDelegate:
方法的“真实”委托的引用:
@interface BFWaveScrollView ()
@property (nonatomic, weak) id<UIScrollViewDelegate> ownDelegate;
@end
将委托设置为 self 以收到有关滚动事件的通知。使用super
,所以setDelegate:
调用的是原始实现,而不是我们修改过的。
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[super setDelegate:self];
}
return self;
}
覆盖setDelegate:
以保存对“真实”委托的引用。
- (void)setDelegate:(id<UIScrollViewDelegate>)delegate {
_ownDelegate = delegate;
}
当UIScrollView尝试调用其委托的方法时,它会首先检查委托是否respondsToSelector:
。如果选择器是UIScrollViewDelegate
协议的一部分,我们必须将其转发给真正的委托(不要忘记#import <objc/runtime.h>
)。
- (BOOL)selectorIsScrollViewDelegateMethod:(SEL)selector {
Protocol *protocol = objc_getProtocol("UIScrollViewDelegate");
struct objc_method_description description = protocol_getMethodDescription(
protocol, selector, NO, YES);
return (description.name != NULL);
}
- (BOOL)respondsToSelector:(SEL)selector {
if ([self selectorIsScrollViewDelegateMethod:selector]) {
return [_ownDelegate respondsToSelector:selector] ||
[super respondsToSelector:selector];
}
return [super respondsToSelector:selector];
}
最后,将所有未在子类中实现的委托方法转发给真正的委托:
- (id)forwardingTargetForSelector:(SEL)selector {
if ([self selectorIsScrollViewDelegateMethod:selector]) {
return _ownDelegate;
}
return [super forwardingTargetForSelector:selector];
}
不要忘记手动转发那些由子类实现的委托方法。