检测UIWebView滚动视图的contentSize变化

17

我想把一个UIView放在UIScrollView的内容底部,我将该视图的位置设置为scrollview的contentsize高度。但是我的scrollview是UIWebView的子视图,所以当图片加载时,contentsize的高度会改变,导致本应在scrollview底部的视图出现在中间...

因此,我正在寻找一种方法,在scrollview的contentsize发生更改时得到通知。我尝试了继承它并更改contentsize的setter方法来发送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:] in MyClass.o ld:无法找到架构armv7的符号

有没有想法应该如何对setter进行子类化?

谢谢!

2个回答

32

也许您可以使用键值观察(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的内容吗? - Reinier Melian
使用Swift 4中的WKWebView,当我滚动时,通知会多次发送。 虽然内容大小实际上并没有改变,但是反复滚动会发送通知。 这是预期行为吗? - hidden-username
你认为使用Swift对WKWebView更新contentSize的频率有任何影响吗?无论如何,如果这是一个问题,可以将先前的contentSize存储在实例变量中,只有在新的大小实际上不同的情况下才采取行动。 - rob mayoff

3
你的方法是半正确的。你确实可以通过类别覆盖现有方法,但你不能访问类的实例变量。
在这种情况下,你需要使用方法交换:你重写setContentSize方法,同时保留对原始方法的引用,以便在设置_contentSize值时调用它。
以下是代码示例,带有注释:
@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

希望这能帮助到您。

非常感谢您的精彩回答。但我有一个问题。如果我有多个对象实例,如何检测调用此方法的是哪个实例? - Olcay Ertaş
@OlcayErtaş:你可以使用postNotificationName:object:userInfo:并将self作为userInfo或其他有用的信息发送:https://developer.apple.com/reference/foundation/nsnotificationcenter/1410608-postnotificationname?language=objc - sergio
我也是这样做的。谢谢你的回复。 - Olcay Ertaş

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接