效果图
-
向上滚动图片变窄
-
向上滚动图片不变
源码地址在我的github
前言
目前主流APP的个人主页都会在顶部展示一个头图,图片可以由用户自定义设置,不仅彰显了用户的个性,也增加了页面的丰富性;并且头图也可以随着页面的滚动有一种缩放的效果,大大的提高了用户的交互性,让用户爱不释手!如果你正在负责这方面的需求,我觉得你应该尝试下这个效果。
实现原理
核心原理很简单,就是UIView
的contentMode
属性。针对UIImageView有常用的三种mode:UIViewContentModeScaleToFill
、UIViewContentModeScaleAspectFit
、UIViewContentModeScaleAspectFill
,用三个图片展示它们的区别吧。
-
UIViewContentModeScaleToFill
这个就比较暴力了,按着UIImageView的尺寸将图片不按着原图比例填满,所以出现了比较丑的压扁效果。
-
UIViewContentModeScaleToFill
将图片进行等比例缩放,直到宽或者高和UIImageView的一样。所以高度先合适,宽度只能留白了。
-
UIViewContentModeScaleAspectFill
将图片进行等比例缩放,直到填满整个UIImageView,这个效果正是我们想要的。
综上所述,我们将UIImageView.contentMode
设置为UIViewContentModeScaleAspectFill
,然后随着滚动的offsetY去更新UIImageView的y和height就行了。
代码展示
我们开发的时候,对于视图的布局一般有三种方式:
直接设置视图的frame,不进行约束;
使用代码约束,常用的masonry;
使用xib约束;
选择不同的布局方式,在实现效果的细节上也是不一样的,所以我针对这三种方式进行分别展示。当你的项目选择一种适合的布局方式,然后选择效果的实现方式即可。直接设置视图的frame,不进行约束
初始化视图
- (void)initUINoConstraint
{
UIImageView *headerImageView = [[UIImageView alloc] initWithFrame:self.bounds];
headerImageView.clipsToBounds = YES;
headerImageView.contentMode = UIViewContentModeScaleAspectFill;
headerImageView.image = [UIImage imageNamed:@"lufei.jpg"];
[self addSubview:headerImageView];
self.headerImageView = headerImageView;
self.originalHeaderImageViewFrame = self.bounds;
}
根据offsetY更新布局
- (void)updateHeaderImageViewFrameWithOffsetY:(CGFloat)offsetY
{
//防止height小于0
if (self.originalHeaderImageViewFrame.size.height - offsetY < 0) {
return;
}
//如果不使用约束的话,图片的y值要上移offsetY,同时height也要增加offsetY
CGFloat x = self.originalHeaderImageViewFrame.origin.x;
CGFloat y = self.originalHeaderImageViewFrame.origin.y + offsetY;
CGFloat width = self.originalHeaderImageViewFrame.size.width;
CGFloat height = self.originalHeaderImageViewFrame.size.height - offsetY;
self.headerImageView.frame = CGRectMake(x, y, width, height);
}
- 使用代码约束,常用的masonry
初始化视图
- (void)initUICodeConstraint
{
UIImageView *headerImageView = [[UIImageView alloc] init];
headerImageView.clipsToBounds = YES;
headerImageView.contentMode = UIViewContentModeScaleAspectFill;
headerImageView.image = [UIImage imageNamed:@"lufei.jpg"];
[self addSubview:headerImageView];
self.headerImageView = headerImageView;
//约束设置为:跟父视图左、下、右贴紧,再约束高度,所以更新高度约束的时候会向上增加,xib约束同理
[headerImageView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.and.bottom.equalTo(self).offset(0);
self.codeConstraintHeight = make.height.equalTo(@(self.bounds.size.height));
}];
self.originalHeaderImageViewHeight = self.bounds.size.height;
}
根据offsetY更新布局
- (void)updateHeaderImageViewFrameWithOffsetY:(CGFloat)offsetY
{
//防止height小于0
if (self.originalHeaderImageViewHeight -offsetY < 0) {
return;
}
//第一种方式:获取到这个约束,直接对约束值修改
//self.codeConstraintHeight.equalTo(@(self.originalHeaderImageViewHeight -offsetY));
//第二种方式:直接使用masonry提供的更新约束方法,其实原理是一样的
[self.headerImageView mas_updateConstraints:^(MASConstraintMaker *make) {
make.height.equalTo(@(self.originalHeaderImageViewHeight -offsetY));
}];
}
- 使用xib约束
//需要将xib中的高度约束用拉线拉出来
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *layoutHeightOfHeaderImageView;
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
self.isNeedNarrow = YES;
[self initUIXibConstraint];
}
return self;
}
根据offsetY更新布局
- (void)updateHeaderImageViewFrameWithOffsetY:(CGFloat)offsetY
{
//防止height小于0
if (self.originalHeaderImageViewHeight -offsetY < 0) {
return;
}
self.layoutHeightOfHeaderImageView.constant = self.originalHeaderImageViewHeight - offsetY;
}
- 向上滚动图片是否变窄的实现
//用于实现向上滚动的时候,图片不变窄
- (void)updateHeaderImageViewFrameWithOffsetY:(CGFloat)offsetY
{
if (!self.isNeedNarrow && offsetY > 0) {
return;
}
}
外部使用
//没有约束
// ZoomHeaderView *headerView = [[ZoomHeaderView alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 300) type:ZoomHeaderViewTypeNoConstraint];
//代码约束
// ZoomHeaderView *headerView = [[ZoomHeaderView alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 300) type:ZoomHeaderViewTypeCodeConstraint];
//xib约束
ZoomHeaderView *headerView = [[[NSBundle mainBundle] loadNibNamed:@"ZoomHeaderView" owner:nil options:nil] lastObject];
//可以对比看效果
// headerView.isNeedNarrow = NO;
tableView.tableHeaderView = headerView;
self.headerView = headerView;
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGFloat offsetY = scrollView.contentOffset.y;
[self.headerView updateHeaderImageViewFrameWithOffsetY:offsetY];
}
总结
总得来说,代码还是比较简单的,可以快捷实现滚动缩放的效果,只是要注意一些细节设置,欢迎大家去我的github,下载源码进行查看。如何我的描述中有什么纰漏或者错误的地方,麻烦各位大神多多指正!