在iOS產(chǎn)品中,特別是個人主頁里面,下拉放大頂部圖片的效果很常見,也很炫酷(請允許我自戀一下,在公司項目中,我額外增加了這個效果,老大就說特別好看,可惜安卓實現(xiàn)不出來,hiahiahia,到這里有木有為iOS的風(fēng)格而自豪),今天就簡單講一下實現(xiàn)這種效果的思路:
1.首先得有個tableView,我處理的方式是設(shè)置tableView的contentInset,在top空出一片圖片的原始區(qū)域,
profileTab.contentInset = UIEdgeInsetsMake(profileHeader.frame.height, 0, 0, 0)
2.下一步就是監(jiān)聽滾動代理的時候的處理,這里面主要注意的是要時刻改變頂部視圖的尺寸,說白了就是y值或者frame值
當(dāng)tableView下拉的時候,要改變height和y,具體做法如下
let offset = scrollView.contentOffset.y + scrollView.contentInset.top
var newFrame = profileHeader.frame
newFrame.size.height = scaleToHeight(size: 230)
newFrame.origin.y = -((scaleToHeight(size: 230) < offset) ? scaleToHeight(size: 230) : offset)
profileHeader.frame = newFrame
ps:這里的scaleToHeight(size:CGFloat)是我自己封裝的根據(jù)寬高適配屏幕
3.如下是向上滾動,就要縮短其高度了
let offset = scrollView.contentOffset.y + scrollView.contentInset.top
var newFrame = profileHeader.frame
if offset < 0 {
newFrame.origin.y = 0
newFrame.size.height = scaleToHeight(size: 230) - offset
profileHeader.frame = newFrame
target.updateOpaueViewAlpha(alpha: 0.0)
}
整體的代碼如下:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let offset = scrollView.contentOffset.y + scrollView.contentInset.top
var newFrame = profileHeader.frame
if offset < 0 {
newFrame.origin.y = 0
newFrame.size.height = scaleToHeight(size: 230) - offset
profileHeader.frame = newFrame
target.updateOpaueViewAlpha(alpha: 0.0)
}
else {
newFrame.size.height = scaleToHeight(size: 230)
newFrame.origin.y = -((scaleToHeight(size: 230) < offset) ? scaleToHeight(size: 230) : offset)
profileHeader.frame = newFrame
if offset > scaleToHeight(size: 230) {
target.updateOpaueViewAlpha(alpha: 1.0)
}else {
target.updateOpaueViewAlpha(alpha: (offset / scaleToHeight(size: 230)))
}
}
}
對于這個實現(xiàn)思路其實不難,主要就是理解contentInset這個屬性,scrollView(當(dāng)然包括繼承于它的tableView,collectionView)有contentInset,contentOffset,contentSize幾個屬性,我查閱資料這樣理解的:
contentInset不屬于scrollView滾動區(qū)域,可以變態(tài)理解為scrollview之外的部分,這一部分就是一個簡單的空白區(qū),scrollview真實的尺寸是從空白區(qū)域之外開始算起的
contentOffset 就是偏移量,它的滾動不包括上面的contentInset,有興趣的伙伴可以打印試一下,也可以去網(wǎng)上搜索一些大神寫的博客,我記得有一篇很詳細(xì),還上了圖
contentSize 就是scrollView能滾動的范圍
tableView中的tableHeaderView,sectionHeader,tableFooterView,sectionFooter,以及cell都處在contentSize中,在計算contentOffset的時候也要考慮到它們
最后,值得一提的是,對于單純的下拉放大,筆者覺得上面的方法已經(jīng)很好,而對于既有下拉放大,又有區(qū)頭懸浮在導(dǎo)航欄之下,而系統(tǒng)導(dǎo)航欄又隱藏自定義導(dǎo)航欄,的這種需求,真的是讓人頭疼,筆者在經(jīng)過一番處理后也實現(xiàn)了這種效果,下面張貼出來代碼(這是根據(jù)KVO監(jiān)聽contentOffset屬性做的),當(dāng)然筆者的方法可能僅僅適用于項目中,有比較通用的方法的伙伴希望能不吝賜教,分享出來一起學(xué)習(xí)進步:
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "contentOffset" {
let offset = change?[NSKeyValueChangeKey.newKey] as! CGPoint
if offset.y <= 0 && -offset.y >= 64 {
let newFrame = CGRect(x: 0, y: 0, width: kScreenW, height: -offset.y)
teacherView.frame = newFrame
if -offset.y < 64 {
teacherTab.contentInset = UIEdgeInsetsMake(-offset.y, 0, scaleToHeight(size: 20), 0)
}else {
if -offset.y < 82 {
teacherTab.contentInset = UIEdgeInsetsMake(scaleToHeight(size: 228) + offset.y, 0, scaleToHeight(size: 20), 0)
}else {
teacherTab.contentInset = UIEdgeInsetsMake(scaleToHeight(size: 228), 0, scaleToHeight(size: 20), 0)
}
}
target.updateOpaueViewAlpha(alpha: (offset.y + scaleToHeight(size: 228)) / (scaleToHeight(size: 228) - 64))
}else {
let newFrame = CGRect(x: 0, y: 0, width: kScreenW, height:10)
teacherView.frame = newFrame
teacherTab.contentInset = UIEdgeInsetsMake(64, 0, scaleToHeight(size: 20), 0)
target.updateOpaueViewAlpha(alpha: 1.0)
}
}
}