最近一個(gè)項(xiàng)目中,需要一個(gè)類(lèi)似微博個(gè)人頁(yè)滑動(dòng)效果,這里提供一種簡(jiǎn)易實(shí)現(xiàn)思路掏熬。如果有更好的實(shí)現(xiàn)思路搀崭,歡迎探討~
實(shí)現(xiàn)效果
廢話不多說(shuō)叨粘,先附上swift版demo地址:https://github.com/misstang/WBProfileDemo
整體效果解析
整體效果可以解刨為四個(gè)效果
下拉底層tableView header放大,這個(gè)一大堆博客解析瘤睹,我這里就不啰嗦啦升敲。
滑動(dòng)底層tableView的時(shí)候改變navigationBar的透明度,這里navigationBar隱藏與否都可以實(shí)現(xiàn)轰传,不隱藏可以參考IOS之UI--動(dòng)態(tài)設(shè)置NavigationBar的顏色以及透明度驴党,demo里也是不隱藏的,當(dāng)然了获茬,隱藏navigationBar港庄,自定義View是最簡(jiǎn)單的。
左右滑動(dòng)頂層四個(gè)tableView的時(shí)候锦茁,底層tableView section上的四個(gè)tab跟著滑動(dòng)的scrollView滾動(dòng)攘轩,這里section上的四個(gè)tab用的是HMSegmentedControl。
底層和頂層tableView的滾動(dòng)效果码俩,頂層和底層的tableView根據(jù)偏移量來(lái)決定誰(shuí)滾動(dòng)度帮,這里接下來(lái)會(huì)詳細(xì)講解思路。
兩層tableView滾動(dòng)效果的思路
首先,這里底層為啥要用tableView來(lái)負(fù)責(zé)上下的滾動(dòng)而不是scrollView呢笨篷。我當(dāng)時(shí)是因?yàn)樾Ч杏袀€(gè)類(lèi)似tableView section的東西瞳秽,如果用tableView,那么這個(gè)sectionView不用自己約束率翅,直接使用tableView section就好
然后练俐,開(kāi)始搭建UI架構(gòu)
Demo UI層次
整體層次為三層,由底到頂依次為:
- 負(fù)責(zé)header放大冕臭,變換navigationBar透明度腺晾,整體上下滑動(dòng)的tableView
- 負(fù)責(zé)頂層(數(shù)據(jù)顯示)tableView左右滑動(dòng)的ScrollView
- 負(fù)責(zé)數(shù)據(jù)顯示和上下滑動(dòng)的tableView(放在底層tableView的cell上,cell的高度為底層tableView滑動(dòng)到頂部時(shí)辜贵,頂層tableView應(yīng)有的高度)
最后實(shí)現(xiàn)兩層tableView滾動(dòng)效果悯蝉,從效果中可以總結(jié)出兩層tableView的滾動(dòng)關(guān)系
- 兩層tableView不能同時(shí)滾動(dòng)
- 底層的tableView,它c(diǎn)ontentOffSet.y的范圍為0 到 tableViewHeaderHeight - kTopHeight(navigationBar 加上 statusBar的高度)托慨。
-頂層的tableView要想滾動(dòng)鼻由,那么底層tableView的contentSetOffset.y一定是為最大值的
從上面兩個(gè)約束關(guān)系中,我們可以在兩層tableView中的UIScrollDelegate的scrollViewDidScroll方法中分別加入以下偏移量計(jì)算
- 底層tableView
let contentOffset = headerViewHeight - navigationHeight
//只要是頂層tableView滾動(dòng)了厚棵,那么底層tableView一定是頂在上面蕉世,而不能滾動(dòng)
if let childScrollView = childScrollView, childScrollView.contentOffset.y > 0 {
scrollView.contentOffset.y = contentOffset
/ /如果頂層tableView沒(méi)有滾動(dòng)并且底層tableView還在滾動(dòng),那么就發(fā)通知讓所有頂層的tableView的contentOffSet.y設(shè)置為0
} else if scrollView.contentOffset.y < contentOffset {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: kWBProfileViewControllerTableViewDidScrollNotification), object: nil, userInfo: nil)
}
- 頂層tableView
if self.scrollView == nil {
self.scrollView = scrollView
}
// 發(fā)通知告訴底層tableView婆硬,頂層tableView滾動(dòng)了狠轻,并且把滾動(dòng)的scrollView傳過(guò)去,讓底層tableView根據(jù)第三個(gè)約束條件來(lái)判斷是否讓它的contentOffSet.y可以變化(能否滾動(dòng))
NotificationCenter.default.post(name: NSNotification.Name(rawValue: kProfileSubTableViewControllerScrollViewDidScrollNotification), object: nil, userInfo: [kProfileSubTableViewControllerSubScrollViewInfo : scrollView])
實(shí)現(xiàn)過(guò)程中的主要問(wèn)題
這里主要說(shuō)上下兩個(gè)tableView滾動(dòng)的手勢(shì)沖突的問(wèn)題柿祈。從iOS的響應(yīng)者鏈中我們知道哈误,默認(rèn)一個(gè)手勢(shì)只有一個(gè)響應(yīng)者,也就是說(shuō)上下兩層tableView同時(shí)只有一個(gè)在處理滑動(dòng)的手勢(shì)躏嚎,那么如何在滾動(dòng)的時(shí)候,兩層tableView都能響應(yīng)滑動(dòng)手勢(shì)菩貌,調(diào)用scrollViewdidScroll這個(gè)方法呢卢佣。
通過(guò)測(cè)試UIGestureDelegate頭文件里的方法,最終可以用以下代碼來(lái)讓兩層tableView都響應(yīng)滑動(dòng)的手勢(shì)箭阶,具體代碼如下(也可以參見(jiàn)demo):
//底層tableView實(shí)現(xiàn)這個(gè)UIGestureRecognizerDelegate的方法虚茶,從而可以接收并響應(yīng)上層tabelView的滑動(dòng)手勢(shì),otherGestureRecognizer就是它上層View也持有的Gesture仇参,這里在它上層的有scrollView和頂層tableView
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
// 保證其它手勢(shì)的View存在
guard let otherView = otherGestureRecognizer.view else {
return false
}
//如果其它手勢(shì)的View是scrollView的手勢(shì)嘹叫,肯定是不能同時(shí)響應(yīng)的
if otherView.isMember(of: UIScrollView.self) {
return false
}
// 其它手勢(shì)是scrollView子類(lèi)的pan手勢(shì) ,那么就讓它們同時(shí)響應(yīng)
let isPan = gestureRecognizer.isKind(of: UIPanGestureRecognizer.self)
if isPan && otherView.isKind(of: UIScrollView.self) {
return true
}
return false
}
最后诈乒,如果有更好的思路罩扇,歡迎探討~