美文网首页
iOS下拉-回力弹效果

iOS下拉-回力弹效果

作者: 小宝二代 | 来源:发表于2019-06-13 10:18 被阅读0次

    前言

    在项目开发过程中,UI设计师为了用户体验会经常使用一些动画效果,今天给大家介绍的是下拉回力弹效果,有时也被形象的称为QQ弹效果,使用这一效果比较典型的是QQ,在QQ联系人个人资料页面就使用了这一效果,如下图:


    QQ个人封面.jpg

    实现

    1. 使用可以滚动的控件,比如UIScrollView或UITableView,在上面添加一个UIImageView。
    2. 设置子控件UIImageView的frame,特别是y值和height,以及父控件的contentInset属性。
    3. 利用滚动控件的代理方法scrollViewDidScroll处理UIImageView的frame和bounds。
    4. 设置UIImageView上子控件的布局。

    步骤一:

    • 添加滚动父视图,这里我用的UITableView
    #import "ViewController.h"
    #import "MyHeaderView.h"
    #import "MyFooterView.h"
    
    #define Height  170
    #define SCREEN_WIDTH      ([UIScreen mainScreen].bounds.size.width)
    
    @interface ViewController () <UITableViewDelegate, UITableViewDataSource>
    
    @property (nonatomic, strong) UITableView *tableView;
    @property (nonatomic, strong) MyHeaderView *headerView;
    @property (nonatomic, strong) MyFooterView *footerView;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view.
        
        if (@available(iOS 11.0, *)) {
            self.tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
        } else {
            self.automaticallyAdjustsScrollViewInsets = NO;
        }
        
        [self.view addSubview:self.tableView];
    }
    
    
    #pragma mark - Getter
    - (UITableView *)tableView {
        if (_tableView) return _tableView;
        _tableView = [[UITableView alloc] initWithFrame:self.view.bounds];
        [_tableView setDelegate:self];
        [_tableView setDataSource:self];
        [_tableView setRowHeight:40];
        [_tableView setContentInset:UIEdgeInsetsMake(Height, 0, 0, 0)];
        //在_tableView的contentOffset不是CGPointMake(-Height, 0)时可以使用
    //    [_tableView setContentOffset:CGPointMake(-Height, 0)];
        [_tableView addSubview:self.headerView];
        [_tableView setTableFooterView:self.footerView];
        return _tableView;
    }
    

    步骤二:

    • 在父视图上添加UIImageView,这里y值要设置为高度的负值
     #pragma mark - Getter
    - (UITableView *)tableView {
        if (_tableView) return _tableView;
        _tableView = [[UITableView alloc] initWithFrame:self.view.bounds];
        [_tableView setDelegate:self];
        [_tableView setDataSource:self];
        [_tableView setRowHeight:40];
    
        //设置_tableView的回弹范围增加Height
        [_tableView setContentInset:UIEdgeInsetsMake(Height, 0, 0, 0)];
    
        //在_tableView的contentOffset不是CGPointMake(-Height, 0)时可以使用
        //也即是_tableView的起始位置不是-Height时使用
        // [_tableView setContentOffset:CGPointMake(-Height, 0)];
    
        //注意:这里使用的是addSubview
        [_tableView addSubview:self.headerView];
        [_tableView setTableFooterView:self.footerView];
        return _tableView;
    }
    
    - (MyHeaderView *)headerView {
        if (_headerView) return _headerView;
        //由于_tableView的属性contentInset的设置,使_tableView刚开始的时候就向下拉了Height的高度
        //所以_tableView左上角的bounds.origin.y的值为-Height
        //要想把_headerView固定在父视图的左上角,就需要_headerView.frame.origin.y与_tableView.bounds.origin.y相等
        //因为在下拉过程中会改变_tableView.bounds.origin.y值
        _headerView = [[MyHeaderView alloc] initWithFrame:CGRectMake(0, -Height, self.view.bounds.size.width, Height)];
        return _headerView;
    }
    

    步骤三:

    • 这一步是核心,直接看代码
    #pragma mark - UIScrollViewDelegate
    - (void)scrollViewDidScroll:(UIScrollView *)scrollView {
        
        NSLog(@"====================%@",NSStringFromCGRect(scrollView.bounds));
        NSLog(@"====================%@",NSStringFromCGPoint(scrollView.contentOffset));
        
        CGFloat y = scrollView.contentOffset.y;
        CGFloat x = y + Height;
        
        if (y < - Height) {
            
            CGRect frame  = self.headerView.frame;
            CGRect bounds = self.headerView.bounds;
            
            //固定左上角
            frame.origin.y    = y;
            //增加高度,出现qq弹效果
            frame.size.height = -y;
            
            //水平方向等比放大
            frame.origin.x    = x;
            frame.size.width  = SCREEN_WIDTH + fabs(x) * 2;
            
            //使UIImageView上的子控件不会随父控件向左移动
            bounds.origin.x        = x;
            
            self.headerView.bounds = bounds;
            self.headerView.frame  = frame;
        }
    }
    

    步骤四

    • UIImageView上子控件的布局,这里会用到一个属性AutoresizingMask,这个属性会自动调节子控件在父控件中的位置和宽高,可以参考这里的详解:https://www.jianshu.com/p/704c58bee3bd
    #import "MyHeaderView.h"
    
    @interface MyHeaderView ()
    
    @property (nonatomic, strong) UILabel *titleLabel;
    
    @end
    
    @implementation MyHeaderView
    
    - (instancetype)initWithFrame:(CGRect)frame {
        if (self = [super initWithFrame:frame]) {
            [self setupUI];
        }
        return self;
    }
    
    - (void)setupUI {
        
        [self setAutoresizesSubviews:YES];
        [self setUserInteractionEnabled:YES];
        [self setImage:[UIImage imageNamed:@"ic_top_bg"]];
        
        [self addSubview:self.titleLabel];
    }
    
    - (UILabel *)titleLabel {
        if (_titleLabel) return _titleLabel;
        _titleLabel = [[UILabel alloc] init];
        [_titleLabel setFrame:CGRectMake(100, 100, 100, 20)];
        [_titleLabel setTextColor:[UIColor whiteColor]];
        [_titleLabel setFont:[UIFont systemFontOfSize:14]];
        [_titleLabel setText:@"QQ弹效果"];
        //这里用了UIViewAutoresizingFlexibleTopMargin,自动调整view与父视图上边距,以保证下边距不变
        [_titleLabel setAutoresizingMask:UIViewAutoresizingFlexibleTopMargin];
        return _titleLabel;
    }
    
    @end
    
    • 这里也是可以用Masonry布局,不过要注意上下边距哪一边要固定,效果跟属性AutoresizingMask类似,小心重复使用。

    效果图

    QQ弹效果图.gif

    结语

    做此笔记方便对基本知识的总结应用,比如frame、bounds、center、transform等属性的应用,以及UIScrollView的三个属性的应用。如有问题,欢迎留言交流。

    相关文章

      网友评论

          本文标题:iOS下拉-回力弹效果

          本文链接:https://www.haomeiwen.com/subject/haljfctx.html