2010-03-16 83 views
19

有没有办法得到一个通知,回调或其他方式来调用一个方法,每当一个UIView变得可见的用户,即当一个UIScrollview是一些UIViews的超级视图,并且这样一个UIView的ViewController应该得到当用户看到它的视图时通知它?如何在UIView变得可见时得到通知?

我知道的可能,但检查不那么优雅的解决了滚动滚动哪个位置(通过UIScrollViewDelegate的方法)和计算,如果子视图中的任何一个可见...
但我正在寻找以更普遍的方式来做到这一点。

+0

我的应用程序的导航基于水平滚动UIScrollView。我也通过分类的UIWindow拦截触摸。因此,当前可见视图的ViewController需要将自己注册为委托给子类UIWindow。这就是我希望在视图变得可见时得到通知的原因。 – 2010-03-17 21:09:52

回答

8

如果您的视图呈现行为,它应该位于视图控制器内。在视图控制器上,每次出现视图时都会调用viewDidAppear方法。

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

viewDidAppear的问题在于,它仅在使用导航控制器时被调用。在我的情况下,视图从屏幕上滚动*滚动,然后滚动回来。滚动似乎不触发viewDidAppear .... – 2010-03-19 11:19:56

+1

那么,什么可以表明视图是否出现在屏幕上与滚动? – 2014-05-21 08:01:12

+0

“它应该在视图控制器内。” - 并且视图控制器仅适用于全屏视图。有时候我们只能看到屏幕的一部分。 – Jonny 2016-02-09 06:29:28

0

视图的图层属性应该告诉我们,如果这种观点是可见或不可见

[view.layer visibleRect]; 

但心不是为我工作。

因此,解决方法是使用UiScrollView contentOffset属性来计算特定的视图是否可见。

+1

那么如何添加一个触发器,当该值发生变化时触发? – 2015-08-07 14:55:26

+0

很久以前我就回答了这个问题,即使我忘记了上下文。 子类UIView并使用以下方法。 https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/#//apple_ref/doc/uid/TP40006816-CH3-SW139 – 2015-08-10 11:31:36

+0

这就是我现在正在做的。我想听一门外部课程。 – 2015-08-17 19:43:45

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”上的任何变化。如果窗口是零,我们将停止观察。您可以将的keyPath更改为任何其他财产,也许“帧”如果在你的superviews没有的UIScrollView:

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

感谢您的详细解决方案。我尝试了一下,但问题是在我有机会移除观察者之前,超级视图正在被释放。我甚至观察他们的“超级观点”关键路径的变化,但它没有帮助。这绝对是KVO最糟糕的部分。 – phatmann 2015-08-27 18:13:58

+0

这应该是没有问题的,我猜。如果superview被释放,观察视图(我们想知道它的视图是否可见)也应该被释放,因为它是子视图。 – Thomas 2015-08-27 22:20:52

+0

你说得对。我的问题是我没有在所有关键路径上调用removeObserver。也就是说,KVO最佳实践是保留所有观察对象的所有权。在这种情况下,这意味着将由'observeSuperviewsOnOffsetChange'创建的超级视图列表放入成员变量中,并在'removeAsSuperviewObserver'中使用此保留的超级列表列表。这也有轻微的性能优势。 – phatmann 2015-08-28 19:16:33

相关问题