2010-03-16 1 views
19

Есть ли способ получить уведомление, обратный вызов или какие-либо другие способы вызова метода всякий раз, когда UIView становится видимым для пользователя, то есть когда UIScrollview является надзором некоторых UIView, а ViewController такого UIView получает уведомляется, когда его вид теперь отображается пользователю?Как я могу получить уведомление, когда UIView станет видимым?

Я осведомлен о возможности, но не столь элегантное решение проверки в какое положение ScrollView прокручивается (через UIScrollViewDelegate-методу) и вычислить, если либо один из подвидов видны ...
Но я ищу для более универсального способа сделать это.

+0

Навигация по моему приложению основан на прокрутке UIScrollView по горизонтали. Я также перехватываю штрихи через подклассы UIWindow. Поэтому ViewController видимого в настоящее время представления должен регистрироваться в качестве делегата для подкласса UIWindow. И именно по этой причине я хочу получить уведомление, если вид станет видимым. –

ответ

8

Если ваше представление демонстрирует поведение, оно должно находиться в контроллере вида. На контроллере вида метод viewDidAppear будет вызываться каждый раз, когда появляется представление. свойство слоя

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

Проблема с viewDidAppear заключается в том, что она вызывается только при использовании навигационного контроллера. В моем случае, просмотры * прокручиваются * с экрана, а затем прокручиваются назад. Прокрутка, похоже, не вызывает viewDidAppear .... –

+1

Итак, что может означать, что вид отображается на экране с помощью скроллера? –

+0

«он должен находиться в контроллере вида». - и просмотр контроллеров работает только с полноэкранным просмотром. Иногда у нас есть представления, которые охватывают только часть экрана. – Jonny

0

View должен сообщить нам, если эта точка зрения видна или нет

[view.layer visibleRect]; 

, но это разве работает для меня.

Таким образом, чтобы обойти это, можно использовать свойство UIScrollView contentOffset, чтобы вычислить, является ли конкретный вид видимым или нет.

+1

Итак, как вы добавляете триггер, который запускается при изменении этого значения? –

+0

Я уже давно ответил на этот вопрос, даже я забыл контекст. Подкласс UIView и использовать следующие методы. https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/#//apple_ref/doc/uid/TP40006816-CH3-SW139 –

+0

Вот что я сейчас делаю. Я хочу слушать из внешнего класса. –

1

Я не думаю, что существует универсальный способ сделать это для просмотров. Похоже, вы застряли в scrollViewDidEndScrolling и других методах ScrollViewDelegate. Но я не уверен, почему вы говорите, что это элегантно, они довольно просты.

8

мне удалось решить эту проблему таким образом:

Во-первых, добавить категорию для UIView с помощью следующего метода:

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

Тогда, на ваш взгляд, проверьте оконном недвижимости устанавливается:

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

Если он установлен, мы будем наблюдать «contentOffset» каждого надтаблицы на каких-либо изменений. Если окно ничтожно, мы перестанем наблюдать. Вы можете изменить Ключевой путь к любой другой собственности, может быть, «кадр», если нет UIScrollView в ваших 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) { } 
    } 
} 

Теперь осуществить «observeValueForKeyPath» -метод:

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

Наконец, проверьте, рама вида видна внутри рамы окна:

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

Спасибо за подробное решение. Я пытаюсь это сделать, но проблема в том, что супервизоры освобождаются, прежде чем у меня появится шанс удалить их наблюдателя. Я даже наблюдаю за изменениями в их ключевом пути «superview», но это не помогает. Это определенно худшая часть KVO. – phatmann

+0

Это не должно быть проблемой, я думаю. Если супервизор становится освобожденным, наблюдательное представление (представление, о котором мы хотим знать, если оно видимо), также должно быть освобождено, поскольку оно является подчиненным. – Thomas

+0

Вы правы. Моя проблема заключалась в том, что я не смог вызвать 'removeObserver' на всех путях ключей. Тем не менее, наилучшей практикой KVO является сохранение собственности на все наблюдаемые объекты. В этом случае это означает размещение списка супервизоров, созданных 'observSuperviewOOOffsetChange' в переменной-члене, и использование этого списка сохраненных супервидов в' removeAsSuperviewObserver'. Это также имеет небольшое преимущество в производительности. – phatmann