前言
在项目开发过程中,UI设计师为了用户体验会经常使用一些动画效果,今天给大家介绍的是下拉回力弹效果,有时也被形象的称为QQ弹效果,使用这一效果比较典型的是QQ,在QQ联系人个人资料页面就使用了这一效果,如下图:
QQ个人封面.jpg
实现
- 使用可以滚动的控件,比如UIScrollView或UITableView,在上面添加一个UIImageView。
- 设置子控件UIImageView的frame,特别是y值和height,以及父控件的contentInset属性。
- 利用滚动控件的代理方法scrollViewDidScroll处理UIImageView的frame和bounds。
- 设置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的三个属性的应用。如有问题,欢迎留言交流。
网友评论