效果圖
-
向上滾動(dòng)圖片變窄
zoomHeaderImageView.gif -
向上滾動(dòng)圖片不變
zoomHeaderImageViewNoNarrow.gif
源碼地址在我的github
前言
目前主流APP的個(gè)人主頁(yè)都會(huì)在頂部展示一個(gè)頭圖至耻,圖片可以由用戶自定義設(shè)置勋眯,不僅彰顯了用戶的個(gè)性野瘦,也增加了頁(yè)面的豐富性窄绒;并且頭圖也可以隨著頁(yè)面的滾動(dòng)有一種縮放的效果液样,大大的提高了用戶的交互性虾宇,讓用戶愛不釋手旨剥!如果你正在負(fù)責(zé)這方面的需求空扎,我覺得你應(yīng)該嘗試下這個(gè)效果。
實(shí)現(xiàn)原理
核心原理很簡(jiǎn)單赔嚎,就是UIView
的contentMode
屬性膘盖。針對(duì)UIImageView有常用的三種mode:UIViewContentModeScaleToFill
、UIViewContentModeScaleAspectFit
尤误、UIViewContentModeScaleAspectFill
,用三個(gè)圖片展示它們的區(qū)別吧侠畔。
-
UIViewContentModeScaleToFill
這個(gè)就比較暴力了,按著UIImageView的尺寸將圖片不按著原圖比例填滿损晤,所以出現(xiàn)了比較丑的壓扁效果软棺。
UIViewContentModeScaleToFill.png -
UIViewContentModeScaleToFill
將圖片進(jìn)行等比例縮放,直到寬或者高和UIImageView的一樣尤勋。所以高度先合適喘落,寬度只能留白了。
UIViewContentModeScaleAspectFit.png -
UIViewContentModeScaleAspectFill
將圖片進(jìn)行等比例縮放最冰,直到填滿整個(gè)UIImageView瘦棋,這個(gè)效果正是我們想要的。
UIViewContentModeScaleAspectFill.png
綜上所述暖哨,我們將UIImageView.contentMode
設(shè)置為UIViewContentModeScaleAspectFill
赌朋,然后隨著滾動(dòng)的offsetY去更新UIImageView的y和height就行了。
代碼展示
我們開發(fā)的時(shí)候鹿蜀,對(duì)于視圖的布局一般有三種方式:
直接設(shè)置視圖的frame箕慧,不進(jìn)行約束;
使用代碼約束茴恰,常用的masonry颠焦;
使用xib約束;
選擇不同的布局方式往枣,在實(shí)現(xiàn)效果的細(xì)節(jié)上也是不一樣的伐庭,所以我針對(duì)這三種方式進(jìn)行分別展示粉渠。當(dāng)你的項(xiàng)目選擇一種適合的布局方式,然后選擇效果的實(shí)現(xiàn)方式即可圾另。直接設(shè)置視圖的frame霸株,不進(jìn)行約束
初始化視圖
- (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;
}
根據(jù)offsetY更新布局
- (void)updateHeaderImageViewFrameWithOffsetY:(CGFloat)offsetY
{
//防止height小于0
if (self.originalHeaderImageViewFrame.size.height - offsetY < 0) {
return;
}
//如果不使用約束的話,圖片的y值要上移offsetY,同時(shí)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;
//約束設(shè)置為:跟父視圖左去件、下、右貼緊扰路,再約束高度尤溜,所以更新高度約束的時(shí)候會(huì)向上增加,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;
}
根據(jù)offsetY更新布局
- (void)updateHeaderImageViewFrameWithOffsetY:(CGFloat)offsetY
{
//防止height小于0
if (self.originalHeaderImageViewHeight -offsetY < 0) {
return;
}
//第一種方式:獲取到這個(gè)約束汗唱,直接對(duì)約束值修改
//self.codeConstraintHeight.equalTo(@(self.originalHeaderImageViewHeight -offsetY));
//第二種方式:直接使用masonry提供的更新約束方法宫莱,其實(shí)原理是一樣的
[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;
}
根據(jù)offsetY更新布局
- (void)updateHeaderImageViewFrameWithOffsetY:(CGFloat)offsetY
{
//防止height小于0
if (self.originalHeaderImageViewHeight -offsetY < 0) {
return;
}
self.layoutHeightOfHeaderImageView.constant = self.originalHeaderImageViewHeight - offsetY;
}
- 向上滾動(dòng)圖片是否變窄的實(shí)現(xiàn)
//用于實(shí)現(xiàn)向上滾動(dòng)的時(shí)候,圖片不變窄
- (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];
//可以對(duì)比看效果
// headerView.isNeedNarrow = NO;
tableView.tableHeaderView = headerView;
self.headerView = headerView;
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGFloat offsetY = scrollView.contentOffset.y;
[self.headerView updateHeaderImageViewFrameWithOffsetY:offsetY];
}
總結(jié)
總得來說哩罪,代碼還是比較簡(jiǎn)單的授霸,可以快捷實(shí)現(xiàn)滾動(dòng)縮放的效果,只是要注意一些細(xì)節(jié)設(shè)置际插,歡迎大家去我的github,下載源碼進(jìn)行查看碘耳。如何我的描述中有什么紕漏或者錯(cuò)誤的地方,麻煩各位大神多多指正框弛!