How can I get notified when a UIView becomes visible?

后端 未结 4 1771
面向向阳花
面向向阳花 2021-02-02 10:47

Is there a way to get a notification, a callback or some other means to call a method whenever a UIView becomes visible for the user, i.e. when a UIScrollview is the superview o

4条回答
  •  抹茶落季
    2021-02-02 11:24

    I've managed to solve the problem this way:

    First, add a category for UIView with the following method:

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

    Then, in your View, check if the window-property is set:

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

    If it is set, we'll observe the "contentOffset" of each superview on any change. If the window is nil, we'll stop observing. You can change the keyPath to any other property, maybe "frame" if there is no UIScrollView in your 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) { }
        }
    }
    

    Now implement the "observeValueForKeyPath"-method:

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

    Finally, check if the view's frame is visible inside the window's frame:

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

提交回复
热议问题