обнаруживать изменения в contentSize представления прокрутки UIWebView

Я пытаюсь установить UIView внизу содержимого UIScrollView, для этого я установил позицию представления на высоту содержимого scrollview. Но мой scrollview является подвидом UIWebView, поэтому, когда изображения загружаются, высота содержимого изменяется, и мое представление, которое должно быть внизу прокрутки, оказывается посередине ...

Итак, я ищу способ получать уведомления об изменениях содержимого scrollview. Я попытался создать подкласс и изменить установщик содержимого для отправки 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

Есть идеи, как установить подклассы?

Спасибо !


person Abel    schedule 08.11.2012    source источник


Ответы (2)


Возможно, вы можете использовать наблюдение за ключом и значением (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

person rob mayoff    schedule 08.11.2012
comment
@rob mayoff, можешь поделиться этим на swift3? - person Reinier Melian; 06.01.2017
comment
@rob mayoff с WKWebview в Swift 4 уведомление отправляется несколько раз, когда я прокручиваю. Размер содержимого на самом деле не меняется, но при многократной прокрутке уведомления отправляются. Это ожидаемое поведение? - person hidden-username; 04.03.2018
comment
Как вы думаете, почему использование Swift влияет на частоту WKWebView обновлений contentSize? В любом случае, если это проблема для вас, сохраните предыдущий contentSize в переменной экземпляра и принимайте меры только в том случае, если новый размер действительно отличается. - person rob mayoff; 04.03.2018

Ваш подход наполовину правильный. Вы, безусловно, можете переопределить существующий метод через категорию, чего вы не можете сделать, хотя и обращаетесь к ivar класса.

В этом случае вам нужно переключение метода: вы переопределяете 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

Надеюсь, поможет.

person sergio    schedule 08.11.2012
comment
Большое спасибо за этот хороший ответ. Но у меня есть вопрос. Если у меня есть несколько экземпляров объекта, как я могу определить, какой экземпляр вызывает этот метод? - person Olcay Ertaş; 15.12.2016
comment
@ OlcayErtaş: вы можете использовать postNotificationName:object:userInfo: и отправить self как userInfo или любую другую полезную информацию: developer.apple.com/reference/foundation/nsnotificationcenter/ - person sergio; 15.12.2016
comment
Я сделал то же самое. Спасибо за ответ. - person Olcay Ertaş; 16.12.2016