2010-03-16 10 views
19

¿Hay alguna manera de recibir una notificación, una devolución de llamada o algún otro medio para llamar a un método cada vez que UIView se vuelve visible para el usuario, es decir, cuando UIScrollview es la supervista de algunos UIViews, y ViewController de tal UIView obtendrá notificado cuando su vista ahora es visible para el usuario?¿Cómo puedo recibir notificaciones cuando se visualice UIView?

Soy consciente de lo posible, pero no tan elegante solución de verificar a qué posición del ScrollView desplacé (a través de UIScrollViewDelegate-métodos) y calcular si una de las subvistas es visible ...
pero estoy buscando para una forma más universal de hacer esto.

+0

La navegación de mi aplicación se basa en desplazar un UIScrollView horizontalmente. También estoy interceptando toques a través de una ventana UI subclasificada. El ViewController de la vista actualmente visible por lo tanto necesita registrarse como delegado a la ventana de UI subclasificada. Y esta es la razón por la que deseo recibir una notificación si una vista se vuelve visible. –

Respuesta

8

Si su vista muestra un comportamiento, debe estar dentro de un controlador de vista. En un controlador de vista, se llamará al método viewDidAppear cada vez que aparezca la vista. propiedad de capa

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

El problema con viewDidAppear es que solo se llama cuando se utiliza un controlador de navegación. En mi caso, las vistas se * desplazan * fuera de la pantalla, luego se desplazan hacia atrás. El desplazamiento parece no activar viewDidAppear .... –

+1

Entonces, ¿qué puede indicar si la vista aparece en la pantalla con la rueda de desplazamiento? –

+0

"debe estar dentro de un controlador de vista". - y los controladores de vista solo funcionan con vistas de pantalla completa. A veces tenemos vistas que cubren solo una parte de la pantalla. – Jonny

0

de vista debe decirnos si ese punto de vista es visible o no

[view.layer visibleRect]; 

pero esto no es trabajo para mí.

Por lo tanto, conviene utilizar la propiedad UiScrollView contentOffset para calcular si una vista en particular está visible o no.

+1

Entonces, ¿cómo se agrega un disparador que se dispara cuando ese valor cambia? –

+0

Respondí esta pregunta hace mucho tiempo, incluso olvidé el contexto. Subclase UIView y utiliza los siguientes métodos. https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/#//apple_ref/doc/uid/TP40006816-CH3-SW139 –

+0

eso es lo que estoy haciendo ahora. Quiero escuchar de una clase externa. –

1

No creo que haya una forma universal de hacer esto para las vistas. Parece que estás atascado con scrollViewDidEndScrolling y otros métodos ScrollViewDelegate. Pero no estoy seguro de por qué dices que es elegante, son bastante simples.

8

he logrado resolver el problema de esta manera:

En primer lugar, añadir una categoría para UIView con el siguiente método:

// 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; 
} 

Luego, en su opinión, comprobar si la ventana de la propiedad se establece:

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

Si se establece, observaremos el "contentOffset" de cada supervista sobre cualquier cambio. Si la ventana es nula, dejaremos de observar. Puede cambiar el keyPath a cualquier otra propiedad, tal vez "marco" si no hay UIScrollView en sus superviews:

-(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) { } 
    } 
} 

Ahora implementar el "observeValueForKeyPath" -method:

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

Por último, comprobar si el marco de la vista es visible dentro del marco de la ventana:

-(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

Gracias por la solución detallada. Lo estoy probando, pero el problema es que las superviews están siendo desasignadas antes de que tenga la oportunidad de eliminar a su observador. Incluso estoy observando cambios en la ruta de acceso de la "supervista" pero no ayuda. Esta es definitivamente la peor parte de KVO. – phatmann

+0

Esto no debería ser un problema, supongo. Si una supervista se desasigna, la vista de observación (cuya vista queremos saber si es visible), también debe ser desasignada, ya que es una subvista. – Thomas

+0

Tienes razón. Mi problema era que no estaba llamando 'removeObserver' en todas las rutas clave. Dicho esto, la mejor práctica de KVO es conservar la propiedad de todos los objetos observados. En este caso, esto significa colocar la lista de superviews creados por 'observeSuperviewsOnOffsetChange' en una variable miembro, y usar esta lista de superviews retenidas en' removeAsSuperviewObserver'. Esto también tiene un ligero beneficio de rendimiento. – phatmann

Cuestiones relacionadas