iOS instance - slide list to show / hide top view


The project needs an effect: when scrolling down the list, the custom view at the top does not move, and when moving up, the top view is hidden to improve the display range of the list. On this basis, Shanghai adds a dynamic fade in and fade out effect when hiding the list, as follows:


The key point of implementation is that the top view should scroll with the scroll of the list, and the list can scroll to the top of the screen at the top, and the bottom is to scroll to a fixed position, so it will no longer scroll down. As for the gradient effect, as long as the scroll can be controlled, the alpha value can be controlled to change with the scroll.

The key is that the top view is not simply placed on the list, nor is it simply used as the header view of the list.

The top view is indeed directly added as a sub view of self.view, but the range of the list also covers the whole screen. In order to avoid the list content being covered by the top view, set the contentoffset value of the list.

It should be noted that the contentoffset value must be set after adding the list to self.view, otherwise it will be invalid. After setting, you may find that it is good at the beginning. As soon as you click the list content, you will return to the top. Don't panic. That's the problem that will be solved later:

    self.tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, SCREENWIDTH, SCREENHEIGHT)];
    self.tableView.delegate = self;
    self.tableView.dataSource = self;
    self.tableView.tableFooterView = [[UIView alloc] init];// Remove redundant list lines
    [self.view addSubview:self.tableView];
    [self.tableView setContentOffset:CGPointMake(0, -200)];

If our top view wants to scroll with the list, we must know the scrolling effect of the list. Here, we add a UIScrollView property to the custom top view class and assign our list to this property during initialization (UITableView is a subclass of UIScrollView):

    OXScrollHeaderView *scrollHeader = [[OXScrollHeaderView alloc] initWithFrame:CGRectMake(0, 0, SCREENWIDTH, 200)];
    scrollHeader.headerScrollView = self.tableView;
    [self.view addSubview:scrollHeader];

You can see that the top view is added directly to self.view.

The content of the view can be defined by myself, so I only put one picture.

For the following of scrolling, we use the KVO key value to observe (you can view) This blog To understand) to do. Here, we use a Delegate of UIView: willMoveToSuperview:, which will be called when our view is added to the parent view. In this proxy method, we add the observation of the contentoffset value of the list, and call the processing method every time the value changes:

#pragma mark - UIView Delegate
// When it is added to the interface, the observation of content offset is added
- (void)willMoveToSuperview:(UIView *)newSuperview {
    [self.headerScrollView addObserver:self forKeyPath:@"contentOffset" options:(NSKeyValueObservingOptionNew) context:Nil];
    self.headerScrollView.contentInset = UIEdgeInsetsMake(BOTTOM, 0, 0, 0);

#pragma mark - KVO
// Key value observation of contentoffset
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    CGPoint newOffset = [change[@"new"] CGPointValue];
//    NSLog(@"%f, %f", newOffset.x, newOffset.y);
    [self updateSubViewsWithScrollOffset:newOffset];

Here, we set contentInset, which is the position of the vertex of the contentview of the scrollview relative to the scrollview. The four parameters represent the pixel length of the distance up, left, bottom and right. In this way, the list will not be moved to be blocked.

In the processing method, we need to do two things. The first thing is to make the height of the TOP view move with the movement of the list, but we need to control the TOP position to which the list is moved and the BOTTOM position to which the list is moved. In fact, this is the Y value corresponding to the low end of the TOP view.

The second thing is to make the top view gradually change with the movement. It is completely transparent when moving to the highest and opaque when moving to the lowest. This alpha value is also calculated according to the moving value:

- (void)updateSubViewsWithScrollOffset:(CGPoint)newOffset {
//    NSLog(@"scrollview inset top:%f",;
//    NSLog(@"new offset before:%f", newOffset.y);
//    NSLog(@"newOffset : %f", newOffset.y);
    float startChangeOffset = -;// -BOTTOM
    // Whether it exceeds the BOTTOM when pulling down. If it exceeds the BOTTOM, keep the BOTTOM unchanged. If it slides upward, whether it is lower than TOP. If yes, keep TOP, that is, slide to the BOTTOM at most and TOP at least
    newOffset = CGPointMake(newOffset.x, newOffset.y < startChangeOffset ? startChangeOffset : (newOffset.y > -TOP ? -TOP : newOffset.y));
//    NSLog(@"new offset after:%f", newOffset.y);
    // y coordinate of the head view
    float newY = - newOffset.y - BOTTOM;//;
    // Move the head view up synchronously with the slide
    self.frame = CGRectMake(0, newY, self.frame.size.width, self.frame.size.height);
    // Calculate alpha to ensure that it is 1 when sliding to BOTTOM and 0 when sliding to TOP
    float d = -TOP - startChangeOffset;
    float alpha = 1 - (newOffset.y - startChangeOffset) / d;
    self.alpha = alpha;
//    NSLog(@"current offset: %f", newOffset.y);

Here, the height of the TOP view in my project is 200, so TOP is set to 0 and BOTTOM is set to 200.

Example project

You can directly download the sample project for detailed understanding and necessary modifications:

View author's home page

Posted by SalientAnimal on Tue, 23 Nov 2021 03:06:17 -0800