【问题标题】:Subclass a zooming UIScrollView without delegate methods子类化没有委托方法的缩放 UIScrollView
【发布时间】:2013-03-27 22:10:15
【问题描述】:

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

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

假设文档是正确的,并且绝对没有(记录的)方法可以在没有委托的情况下实现缩放,我怎么还能保留拥有一个常规的、不相关的委托的可能性?

【问题讨论】:

    标签: ios objective-c cocoa-touch uiscrollview delegates


    【解决方案1】:

    根据 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 &lt;objc/runtime.h&gt;)。

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

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

    【讨论】:

      【解决方案2】:

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

      @interface MyHackishScrollView: UIScrollView {
          id <UIScrollViewDelegate> ownDelegate;
      }
      
      @end
      
      @implementation MyHackishScrollView
      
      - (void)setDelegate:(id <UIScrollViewDelegate>)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
      

      【讨论】:

      • 谢谢!你是对的,它不是很优雅,但我现在也没有看到任何其他解决方案:) 我不喜欢这种方法的是实现所有委托方法。我认为在那种情况下,仅仅转发是不够的。您还必须检查ownDelegate 是否也回复了他们。我使用NSObject的消息转发方法和一些运行时函数实现了这种方法。我将其添加为单独的答案,但我会接受这个以提出正确的想法。
      • @DrummerB 非常好,你从我这里得到了 +1,因为这就是应该的方式。我曾想过这样的事情,但现在是晚上 11 点,我不想理会自动转发... ;-)
      【解决方案3】:

      也许几周后这更容易阅读和理解 :) (拦截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;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-10-01
        • 2012-07-20
        • 2017-12-09
        • 2015-06-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多