16

UIScrollViewのコンテンツの下部にUIViewを設定しようとしています。そうすることで、ビューの位置をscrollviewの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:] in MyClass.o ld:アーキテクチャarmv7のシンボルが見つかりません

セッターをどのようにサブクラス化する必要があるか考えていますか?

ありがとう !

4

2 に答える 2

32

おそらく、Key-Value監視(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

于 2012-11-08T19:04:56.770 に答える
3

あなたのアプローチは半分正しいです。クラスの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

それが役に立てば幸い。

于 2012-11-08T19:12:23.063 に答える