2010-03-16 9 views
19

Gibt es eine Möglichkeit, eine Benachrichtigung, einen Callback oder ein anderes Mittel zum Aufruf einer Methode zu erhalten, wenn eine UIView für den Benutzer sichtbar wird, dh wenn eine UIScrollview die Superview einiger UIViews ist und der ViewController einer solchen UIView benachrichtigt, wenn seine Sicht jetzt für den Benutzer sichtbar ist?Wie kann ich benachrichtigt werden, wenn eine UIView sichtbar wird?

Ich bin mir bewusst, der möglich ist, aber nicht so elegante Lösung zu prüfen, auf die Position der Scroll gescrollt (über UIScrollViewDelegate-Methoden) und berechnen, wenn entweder eine der Subviews sichtbar ist ...
Aber ich bin auf der Suche für eine universellere Art, dies zu tun.

+0

Die Navigation meiner App basiert ein UIScrollView horizontal auf scrollen. Ich halte auch Berührungen über ein unterklassiertes UIWindow ab. Der ViewController der aktuell sichtbaren Ansicht muss sich daher selbst als Delegierter für das untergeordnete UIWindow registrieren. Und das ist der Grund, warum ich benachrichtigt werden soll, wenn eine Ansicht sichtbar wird. –

Antwort

8

Wenn Ihre Ansicht Verhalten zeigt, sollte es in einem View-Controller sein. Auf einem Ansichtscontroller wird die viewDidAppear-Methode jedes Mal aufgerufen, wenn die Ansicht angezeigt wird.

- (void)viewDidAppear:(BOOL)animated 
+7

Das Problem mit viewDidAppear ist, dass es nur bei Verwendung eines Navigation-Controllers aufgerufen wird. In meinem Fall werden die Ansichten vom Bildschirm * gescrollt * und dann zurückgescrollt. Scrolling scheint nicht viewDidAppear .... –

+1

10 Was kann also angezeigt werden, wenn die Ansicht mit Scroller auf dem Bildschirm angezeigt wird? –

+0

"Es sollte in einem View-Controller sein." - und View-Controller arbeiten nur mit Vollbildansichten. Manchmal haben wir Ansichten, die nur einen Teil des Bildschirms abdecken. – Jonny

0

Schicht Eigenschaft Sicht sollte uns sagen, ob diese Ansicht sichtbar ist oder nicht

[view.layer visibleRect]; 

aber das ist nicht für mich arbeiten.

So könnte umgehen UIScrollView contentOffset-Eigenschaft verwendet werden, um zu berechnen, ob bestimmte Ansicht sichtbar ist oder nicht.

+1

Wie fügen Sie einen Auslöser hinzu, der ausgelöst wird, wenn sich dieser Wert ändert? –

+0

Ich habe diese Frage vor langer Zeit beantwortet, sogar ich habe den Kontext vergessen. Unterklasse UIView und folgende Methoden verwenden. https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/#//apple_ref/doc/uid/TP40006816-CH3-SW139 –

+0

das mache ich jetzt. Ich möchte von einer externen Klasse hören. –

1

Ich glaube nicht, dass es eine universelle Möglichkeit gibt, dies für Ansichten zu tun. Klingt so, als ob Sie mit scrollViewDidEndScrolling und anderen ScrollViewDelegate-Methoden festgefahren wären. Aber ich bin mir nicht sicher, warum Sie sagen, dass es elegant ist, sie sind ziemlich einfach.

8

Ich habe es geschaffen, das Problem auf diese Weise zu lösen:

Fügen Sie zunächst eine Kategorie für UIView mit folgenden Methode:

// retrieve an array containing all super views 

-(NSArray *)getAllSuperviews 
{ 
    NSMutableArray *superviews = [[NSMutableArray alloc] init]; 

    if(self.superview == nil) return nil; 

    [superviews addObject:self.superview]; 
    [superviews addObjectsFromArray:[self.superview getAllSuperviews]]; 

    return superviews; 
} 

in Ihrer View Dann prüfen, ob die Fenster-Eigenschaft gesetzt ist:

-(void)didMoveToWindow 
{ 
    if(self.window != nil) 
     [self observeSuperviewsOnOffsetChange]; 
    else 
     [self removeAsSuperviewObserver]; 
} 

Wenn es gesetzt ist, werden wir die „content~~POS=TRUNC“ jeder Superview auf jede Veränderung beobachten. Wenn das Fenster leer ist, hören wir auf zu beobachten. Sie können die keyPath jede andere Eigenschaft ändern, vielleicht "Rahmen", wenn es keine UIScrollView in Ihrem superviews ist:

-(void)observeSuperviewsOnOffsetChange 
{ 
    NSArray *superviews = [self getAllSuperviews]; 
    for (UIView *superview in superviews) 
    { 
     if([superview respondsToSelector:@selector(contentOffset)]) 
      [superview addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:nil]; 
    } 
} 

-(void)removeAsSuperviewObserver 
{ 
    NSArray *superviews = [self getAllSuperviews]; 
    for (UIView *superview in superviews) 
    { 
     @try 
     { 
      [superview removeObserver:self forKeyPath:@"contentOffset"]; 
     } 
     @catch(id exception) { } 
    } 
} 

nun die "observeValueForKeyPath" -Methode implementieren:

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context 
{ 
    if([keyPath isEqualToString:@"contentOffset"]) 
    { 
     [self checkIfFrameIsVisible]; 
    } 
} 

Schließlich prüfen, ob der Rahmen ist der Ansicht, innerhalb des Fensters Rahmen sichtbar:

-(void)checkIfFrameIsVisible 
{ 
    CGRect myFrameToWindow = [self.window convertRect:self.frame fromView:self]; 
    if(myFrameToWindow.size.width == 0 || myFrameToWindow.size.height == 0) return; 
    if(CGRectContainsRect(self.window.frame, myFrameToWindow)) 
    { 
     // We are visible, do stuff now 
    } 
} 
+0

Danke für die detaillierte Lösung. Ich probiere es aus, aber das Problem ist, dass Superviews freigegeben werden, bevor ich eine Chance habe, ihren Beobachter zu entfernen. Ich beobachte sogar Änderungen an ihrem "superview" Schlüsselpfad, aber es hilft nicht. Dies ist definitiv der schlimmste Teil von KVO. – phatmann

+0

Das sollte kein Problem sein, denke ich. Wenn eine Superansicht freigegeben wird, sollte die Beobachtungsansicht (deren Ansicht wir wissen möchten, ob sie sichtbar ist) ebenfalls freigegeben werden, da es sich um eine Unteransicht handelt. – Thomas

+0

Sie haben Recht. Mein Problem war, dass ich 'removeObserver' nicht auf allen Schlüsselpfaden aufrufen konnte. Die KVO-Best Practice besteht darin, das Eigentum an allen beobachteten Objekten beizubehalten. In diesem Fall bedeutet das, die Liste der von 'observetSuperviewsOnOffsetChange' erstellten Superviews in eine Membervariable zu platzieren und diese beibehaltene Superviews-Liste in 'removeAsSuperviewObserver' zu verwenden. Dies hat auch einen leichten Leistungsvorteil. – phatmann

Verwandte Themen