【问题标题】:detect changes to UIWebView's scroll view's contentSize检测 UIWebView 的滚动视图 contentSize 的变化
【发布时间】:2012-10-29 01:06:34
【问题描述】:

我正在尝试在 UIScrollView 的内容底部设置 UIView,因此我将视图的位置设置为滚动视图的 contentsize 高度。但是我的滚动视图是 UIWebView 的子视图,因此当加载图像时,内容大小的高度会发生变化,而我应该位于滚动视图底部的视图最终会出现在中间......

所以我正在寻找一种在滚动视图的内容大小发生变化时得到通知的方法。我尝试将其子类化并更改 contentsize 的设置器以发送 NSNotification:

@implementation UIScrollView (Height)

-(void)setContentSize:(CGSize)contentSize
{
    _contentSize=contentSize;
    [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:@"scrollViewContentSizeChanged" object:nil]];
}

@end

但在编译时我得到错误提示:

“_OBJC_IVAR_$_UIScrollView._contentSize”,引用自: -[UIScrollView(Heigth) setContentSize:] 在 MyClass.o ld: 未找到架构 armv7 的符号

知道 setter 应该如何子类化吗?

谢谢!

【问题讨论】:

    标签: ios user-interface uiscrollview contentsize


    【解决方案1】:

    也许您可以使用键值观察 (KVO) 来检测内容大小的变化。我没试过,但代码应该是这样的:

    static int kObservingContentSizeChangesContext;
    
    - (void)startObservingContentSizeChangesInWebView:(UIWebView *)webView {
        [webView.scrollView addObserver:self forKeyPath:@"contentSize" options:0 context:&kObservingContentSizeChangesContext];
    }
    
    - (void)stopObservingContentSizeChangesInWebView:(UIWebView *)webView {
        [webView.scrollView removeObserver:self forKeyPath:@"contentSize" context:&kObservingContentSizeChangesContext];
    }
    
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
        if (context == &kObservingContentSizeChangesContext) {
            UIScrollView *scrollView = object;
            NSLog(@"%@ contentSize changed to %@", scrollView, NSStringFromCGSize(scrollView.contentSize));
        } else {
            [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
        }
    }
    

    如果这不起作用,您可能需要调整 setContentSize: 方法。方法调配让您的替换方法调用原始方法,这是您将新的内容大小传递给滚动视图所需要做的事情。

    您可以在此处阅读有关方法调配的更多信息:http://www.mikeash.com/pyblog/friday-qa-2010-01-29-method-replacement-for-fun-and-profit.html

    我认为这是最流行的调酒代码:https://github.com/rentzsch/jrswizzle

    【讨论】:

    • @rob mayoff 你能分享一下吗,这个在 swift3 上吗?
    • @rob mayoff 在 swift 4 中使用 WKWebview,当我滚动时会多次发送通知。内容大小实际上并没有改变,但反复滚动会发送通知。这是预期的行为吗?
    • 为什么你认为使用 Swift 会影响WKWebView 更新contentSize 的频率?无论如何,如果这对您来说有问题,请将之前的 contentSize 存储在实例变量中,并且只有在新大小实际上不同时才采取行动。
    【解决方案2】:

    你的方法是正确的。您当然可以通过类别覆盖现有方法,但您不能做的是访问该类的 ivar。

    在这种情况下,您需要的是方法调配:您覆盖 setContentSize,同时保留对该方法的原始实现的引用,因此您可以调用它来设置 _contentSize 值。

    这是您可以使用 cmets 的代码:

    @implementation UIScrollView (Height)
    
    // -- this method is a generic swizzling workhorse
    // -- it will swap one method impl with another and keep the old one
    // under your own impl name
    + (void)swizzleMethod:(SEL)originalSel andMethod:(SEL)swizzledSel {
    
      Method original = class_getInstanceMethod(self, originalSel);
      Method swizzled = class_getInstanceMethod(self, swizzledSel);
      if (original && swizzled)
         method_exchangeImplementations(original, swizzled);
      else
        NSLog(@"Swizzling Fault: methods not found.");
    
    }    
    
    //-- this is called on the very moment when the categoty is loaded
    //-- and it will ensure the swizzling is done; as you see, I am swapping
    //-- setContentSize and setContentSizeSwizzled;
    + (void)load {
      [self swizzleMethod:@selector(setContentSize:) andMethod:@selector(setContentSizeSwizzled:)];
    }
    
    //-- this is my setContentSizeSwizzled implementation;
    //-- I can still call the original implementation of the method
    //-- which will be avaiable (*after swizzling*) as setContentSizeSwizzled
    //-- this is a bit counterintuitive, but correct!
    - (void)setContentSizeSwizzled:(CGSize)contentSize
    {
      [self setContentSizeSwizzled:contentSize];
      [[NSNotificationCenter defaultCenter] postNotification:[NSNotification    notificationWithName:@"scrollViewContentSizeChanged" object:nil]];
    }
    
    @end
    

    希望对你有帮助。

    【讨论】:

    • 非常感谢您的回答。但我有一个问题。如果我有多个对象实例,如何检测哪个实例正在调用此方法?
    • @OlcayErtaş:您可以使用postNotificationName:object:userInfo: 并将self 作为userInfo 或任何其他有用信息发送:developer.apple.com/reference/foundation/nsnotificationcenter/…
    • 我也是这样做的。感谢您的回复。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-06-09
    • 1970-01-01
    • 2017-01-11
    • 2011-10-26
    • 2014-05-12
    • 1970-01-01
    • 2011-05-02
    相关资源
    最近更新 更多