Search code examples
iosuitableviewscrollviewparallaxcontentoffset

uitableview drag down outside screen


I'm basically trying to achieve this effect: http://youtu.be/VBW2i0P11iI

The tableview is a basic one that's pinned to it's superview with autolayout. The view underneath is added with the classic insertSubview:belowSubview: / addChildViewController combo.

I've tried a couple of approaches. What I have now is:

if (scrollOffset >= -scrollView.contentInset.top) {
        self.resultsTableViewContainerTopConstraint.constant = 0;
    } else {
        self.resultsTableViewContainerTopConstraint.constant = MAX(self.resultsTableViewContainerTopConstraint.constant, self.resultsTableViewContainerTopConstraint.constant - scrollDiff);
    }
}

So I'm basically changing the top constraint based on the delta of contentOffset. The problem with this is that the uitableview bounces back so it always gets into the first branch of the if. But even if I solve this problem I feel like I'll just patch it. I'm sure there's a way more elegant way of achieving the effect in the video having the same responsiveness.

Any suggestion will be much appreciated. Thanks


Solution

  • I'm not sure if this is what you want but I hacked up a quick demo:

    ViewController.h

    #import <UIKit/UIKit.h>
    
    @interface ViewController : UIViewController <UITableViewDataSource, UITableViewDelegate, UIScrollViewDelegate>
    
    @property (nonatomic, strong) UIView *headerView;
    @property (nonatomic, strong) UILabel *lblTitle;
    
    @property (nonatomic, strong) UIButton *btnReset;
    
    @property (nonatomic, strong) UIImageView *imageView;
    
    @property (nonatomic, strong) UIScrollView *scrollView;
    @property (nonatomic, strong) UITableView *tableView;
    
    @property (nonatomic, strong) NSLayoutConstraint *scrollViewTopConstraint;
    
    @end
    

    ViewController.m

    #import "ViewController.h"
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
    
        //self.view.backgroundColor = [UIColor whiteColor];
    
        [self initViews];
        [self initConstraints];
    }
    
    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
    }
    
    -(void)initViews
    {
        self.view.backgroundColor = [UIColor colorWithRed:43.0/255.0 green:39.0/255.0 blue:55.0/255.0 alpha:1.0];
    
        self.scrollView = [[UIScrollView alloc] init];
        self.scrollView.backgroundColor = [UIColor clearColor];
        self.scrollView.alwaysBounceVertical = YES;
        self.scrollView.delegate = self;
    
        self.headerView = [[UIView alloc] init];
        self.headerView.backgroundColor = [UIColor colorWithRed:43.0/255.0 green:39.0/255.0 blue:55.0/255.0 alpha:1.0];
        self.headerView.layer.shadowColor = [UIColor blackColor].CGColor;
        self.headerView.layer.shadowOffset = CGSizeMake(0,0);
        self.headerView.layer.shadowOpacity = 0.25;
        self.headerView.layer.shadowRadius = 4;
    
        self.lblTitle = [[UILabel alloc] init];
        self.lblTitle.text = @"HEADER VIEW";
        self.lblTitle.textColor = [UIColor whiteColor];
        self.lblTitle.textAlignment = NSTextAlignmentCenter;
    
        self.btnReset = [[UIButton alloc] init];
        [self.btnReset setTitle:@"Reset" forState:UIControlStateNormal];
        self.btnReset.backgroundColor = [UIColor colorWithRed:0.75 green:0.0 blue:0.0 alpha:1.0];
        self.btnReset.layer.cornerRadius = 5.0;
        [self.btnReset addTarget:self action:@selector(resetView) forControlEvents:UIControlEventTouchUpInside];
    
        self.imageView = [[UIImageView alloc] init];
        self.imageView.backgroundColor = [UIColor colorWithRed:43.0/255.0 green:39.0/255.0 blue:55.0/255.0 alpha:1.0];
    
        self.tableView = [[UITableView alloc] init];
        self.tableView.delegate = self;
        self.tableView.dataSource = self;
        self.tableView.rowHeight = 150.0;
        self.tableView.layer.shadowColor = [UIColor blackColor].CGColor;
        self.tableView.layer.shadowOffset = CGSizeMake(0,-5);
        self.tableView.layer.shadowOpacity = 0.5;
        self.tableView.layer.shadowRadius = 20;
        self.tableView.backgroundColor = [UIColor clearColor];
        self.tableView.scrollEnabled = NO;
        self.tableView.clipsToBounds = NO;
    
    
        [self.headerView addSubview:self.lblTitle];
        [self.headerView addSubview:self.btnReset];
    
        [self.scrollView addSubview:self.tableView];
    
        [self.view addSubview:self.imageView];
        [self.view addSubview:self.scrollView];
        [self.view addSubview:self.headerView];
    
    }
    
    -(void)initConstraints
    {
        self.scrollView.translatesAutoresizingMaskIntoConstraints = NO;
        self.headerView.translatesAutoresizingMaskIntoConstraints = NO;
        self.lblTitle.translatesAutoresizingMaskIntoConstraints = NO;
        self.btnReset.translatesAutoresizingMaskIntoConstraints = NO;
        self.imageView.translatesAutoresizingMaskIntoConstraints = NO;
        self.tableView.translatesAutoresizingMaskIntoConstraints = NO;
    
        id views = @{
                     @"scrollView": self.scrollView,
                     @"headerView": self.headerView,
                     @"lblTitle": self.lblTitle,
                     @"btnReset": self.btnReset,
                     @"imageView": self.imageView,
                     @"tableView": self.tableView
                     };
    
        [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[scrollView]|" options:0 metrics:nil views:views]];
        [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[scrollView]" options:0 metrics:nil views:views]];
    
        [self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.scrollView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeHeight multiplier:1.0 constant:0.0]];
    
    
    
        self.scrollViewTopConstraint = [NSLayoutConstraint constraintWithItem:self.scrollView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1.0 constant:0.0];
    
        [self.view addConstraint:self.scrollViewTopConstraint];
    
    
    
    
        [self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.scrollView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeHeight multiplier:1.0 constant:0.0]];
    
        [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[headerView(320)]|" options:0 metrics:nil views:views]];
    
        [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[headerView(50)]" options:0 metrics:nil views:views]];
    
        [self.scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[tableView(320)]|" options:0 metrics:nil views:views]];
    
        [self.scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-150-[tableView(300)]|" options:0 metrics:nil views:views]];
    
        [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[imageView(320)]|" options:0 metrics:nil views:views]];
        [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[imageView(320)]" options:0 metrics:nil views:views]];
    
        [self.headerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[lblTitle]|" options:0 metrics:nil views:views]];
        [self.headerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[lblTitle]|" options:0 metrics:nil views:views]];
    
        [self.headerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[btnReset(80)]-5-|" options:0 metrics:nil views:views]];
        [self.headerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-8-[btnReset]-8-|" options:0 metrics:nil views:views]];
    }
    
    -(BOOL)prefersStatusBarHidden
    {
        return YES;
    }
    
    #pragma mark - TableView Methods -
    
    -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
        return 2;
    }
    
    -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        static NSString *cellID = @"cell";
    
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
    
        if(cell == nil)
        {
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID];
        }
    
        cell.backgroundColor = [UIColor colorWithRed:0.5 green:0.5 blue:0.5 alpha:1.0];
    
        return cell;
    }
    
    #pragma mark - ScrollView Delegate -
    
    -(void)scrollViewDidScroll:(UIScrollView *)scrollView
    {
        //NSLog(@"scrollView offset = %lf", scrollView.contentOffset.y);
    
        if(scrollView.contentOffset.y <= -145)
        {
            //self.scrollView.scrollEnabled = NO;
    
            self.scrollViewTopConstraint.constant = self.view.bounds.size.height;
    
            [UIView animateWithDuration:0.5 delay:0.0 options:UIViewAnimationOptionCurveEaseOut animations:^{
    
                [self.scrollView layoutIfNeeded];
    
            } completion:^(BOOL finished) {
    
            }];
        }
    }
    
    -(void)resetView
    {
        self.scrollViewTopConstraint.constant = 0;
    
        [UIView animateWithDuration:0.5 delay:0.0 options:UIViewAnimationOptionCurveEaseIn animations:^{
    
            [self.scrollView layoutIfNeeded];
    
        } completion:^(BOOL finished) {
    
        }];
    }
    

    What you get when you open the app:

    screenshot 1

    Then you drag down to the edge, the view snaps open:

    screenshot 2

    Press the red reset button to bring it back up :D