子类化没有委托方法的缩放UIScrollView

我想实现一个UIScrollView子类来呈现一些自定义格式化的内容。 我只是设置滚动视图的模型对象属性,它处理所有必需的布局和渲染以显示内容。

这很好,但现在我想包括缩放。 根据文档,要支持缩放,您必须设置委托并实现viewForZoomingInScrollView:方法。 我想我可以将委托设置为滚动视图本身并在子类中实现该方法。 但这样做我将失去拥有外部委托(如封装UIViewController)的能力,可以通知滚动事件。

假设文档是正确的,并且绝对没有(记录)方法在没有委托的情况下实现缩放,我怎么还能保留定期,无关委托的可能性?

我滥用了这个事实,我是一个子类( 故意 :P)。 所以你可以破解它。 真的很糟糕,我应该对提出这个解决方案感到不舒服。

 @interface MyHackishScrollView: UIScrollView { id  ownDelegate; } @end @implementation MyHackishScrollView - (void)setDelegate:(id )newDel { ownDelegate = newDel; [super setDelegate:self]; } - (UIView *)viewForScrollingInScrollView:(UIScrollView *)sv { return whateverYouWant; } // and then implement all the delegate methods // something like this: - (void)scrollViewDidScroll:(UIScrollView *)sv { [ownDelegate scrollViewDidScroll:self]; } // etc. @end 

基于H2CO3建议保存指向真实委托的隐藏指针并将所有传入消息转发给它,我提出了以下解决方案。

声明一个私有委托变量来存储对传递给setDelegate:方法的“真实”委托的引用:

 @interface BFWaveScrollView () @property (nonatomic, weak) id ownDelegate; @end 

将委托设置为self以获得有关滚动事件的通知。 使用super ,所以调用原始的setDelegate:实现,而不是我们修改过的。

 - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { [super setDelegate:self]; } return self; } 

覆盖setDelegate:以保存对“真实”委托的引用。

 - (void)setDelegate:(id)delegate { _ownDelegate = delegate; } 

UIScrollView尝试调用其委托的方法时,它将首先检查委托respondsToSelector: . 如果选择器是UIScrollViewDelegate协议的一部分,我们必须将它转发给真正的委托(不要忘记#import )。

 - (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]; } 

不要忘记手动转发由子类实现的那些委托方法。

也许这更容易阅读和理解几周后:)(拦截locationManager的示例代码:didUpdateLocations:在子类中)

除了那个设置self作为超类的委托和拦截setDelegate以便将用户的委托保存到mDelegate的相同处理。

编辑:

 -(BOOL)respondsToSelector:(SEL)selector { if (sel_isEqual(selector, @selector(locationManager:didUpdateLocations:))) return true; return [mDelegate respondsToSelector:selector]; } - (id)forwardingTargetForSelector:(SEL)selector { if (sel_isEqual(selector, @selector(locationManager:didUpdateLocations:))) return self; return mDelegate; }