前段時(shí)間接到個(gè)需求晾浴,簡單而言就是模仿支付寶的首頁负乡,唯一的不同就是支付寶首頁的主功能模塊在屏幕上方,而需求中的主功能模塊在屏幕下方脊凰。
頁面分析
第一輪分析
整個(gè)頁面由三個(gè)模塊組成:主功能模塊抖棘、應(yīng)用模塊和信息模塊。
-
當(dāng)頁面由初始狀態(tài)向上滑動(dòng)時(shí),應(yīng)用模塊和信息模塊會(huì)同時(shí)向上滑動(dòng)切省,而主功能模塊的高度會(huì)隨著滑動(dòng)的距離而變化最岗,大功能按鈕逐漸消失,小功能按鈕逐漸顯示朝捆。
-
當(dāng)小功能按鈕完全顯示以后般渡,主功能模塊的高度就會(huì)固定,而信息模塊和應(yīng)用模塊還可以繼續(xù)向上滑動(dòng)芙盘。
- 當(dāng)頁面由初始狀態(tài)下滑時(shí)驯用,主功能模塊和應(yīng)用模塊的位置都會(huì)固定不變,而信息模塊則會(huì)展示刷新效果何陆,并在刷新結(jié)束后恢復(fù)初始狀態(tài)晨汹。
-
不論是上滑還是下滑豹储,信息模塊右側(cè)都會(huì)顯示滾動(dòng)條贷盲。
根據(jù)上述效果進(jìn)行分析:
-
整個(gè)頁面由兩個(gè)UITableView控件組成:
- 由于信息模塊有下拉刷新功能并且右側(cè)顯示有滾動(dòng)條,因此將信息模塊視為tableView1剥扣。
- 由于信息模塊和應(yīng)用模塊可以一起滑動(dòng)巩剖,而主功能模塊僅僅是改變高度,因此信息模塊和應(yīng)用模塊一起組成tableView2钠怯。
- 由于主功能模塊只是高度發(fā)生變化而位置并不變化佳魔,因此可以將主功能模塊添加在主頁面上,并且用與主功能模塊一樣大小的空白view充當(dāng)tableView2的headerView晦炊。
初始狀態(tài)下鞠鲜,由tableView2響應(yīng)上滑事件,tableView1響應(yīng)下滑事件断国。
一旦由tableView2響應(yīng)了上滑事件贤姆,那么在恢復(fù)初始狀態(tài)之前,下滑事件也會(huì)由tableView2來響應(yīng)稳衬。
根據(jù)上述分析不難看出霞捡,整個(gè)頁面效果實(shí)現(xiàn)的關(guān)鍵在于控制響應(yīng)滑動(dòng)事件的主體。暫且不論代碼的復(fù)雜程度薄疚,其實(shí)現(xiàn)后的效果也不盡如人意碧信。
第二輪分析
在第一輪分析的基礎(chǔ)上,參考了一些網(wǎng)上的資料街夭,進(jìn)行了第二輪分析:
- 整個(gè)頁面僅由一個(gè)UITableView控件組成砰碴,其主體就是信息模塊。有一個(gè)小細(xì)節(jié):當(dāng)我們在主功能模塊或應(yīng)用模塊上觸摸下滑時(shí)板丽,信息模塊同樣會(huì)進(jìn)行下拉刷新呈枉。
- 主功能模塊添加在主頁面上,其高度隨tableView滑動(dòng)距離的變化而變化。
- 依次在tableView.headerView上添加應(yīng)用模塊和刷新模塊碴卧。
- 主功能模塊和應(yīng)用模塊正好覆蓋了tableView.headerView弱卡。
具體布局代碼如下:
_tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height - tabBarHeight) style:UITableViewStylePlain] ;
_tableView.delegate = self ;
_tableView.dataSource = self ;
_tableView.separatorStyle = UITableViewCellSeparatorStyleNone ;
[self.view addSubview:_tableView] ;
UIView *tableHeaderView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, movedHeight + fixedNormalHeight)] ;
tableHeaderView.backgroundColor = [UIColor clearColor] ;
_tableView.tableHeaderView = tableHeaderView ;
_tableView.scrollIndicatorInsets = UIEdgeInsetsMake(movedHeight + fixedNormalHeight, 0, 0, 0) ;
_refreshView = [[UIView alloc]initWithFrame:CGRectMake(0, movedHeight + fixedNormalHeight - 50, [UIScreen mainScreen].bounds.size.width , 50)] ;
[_tableView.tableHeaderView addSubview:_refreshView] ;
_movedView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, movedHeight + fixedNormalHeight)] ;
[_tableView addSubview:_movedView] ;
_fixedView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, fixedNormalHeight)] ;
[self.view addSubview:_fixedView] ;
代碼中,_movedView就是應(yīng)用模塊住册,_fixedView就是主功能模塊婶博。
- 滑動(dòng)時(shí):
- 初始狀態(tài)下,若tableView上滑荧飞,應(yīng)用模塊也會(huì)隨之上滑凡人;若tableView下滑,應(yīng)用模塊則會(huì)固定不動(dòng)叹阔,而原本添加在tableView.headerView上的刷新動(dòng)畫就會(huì)顯示出來挠轴。
- 在恢復(fù)初始狀態(tài)之前,若tableView先上滑再下滑耳幢,應(yīng)用模塊也會(huì)隨之下滑岸晦。
具體實(shí)現(xiàn)代碼為:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat tableViewOffSetY = scrollView.contentOffset.y ;
if(tableViewOffSetY >= 0) {
_movedView.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, movedHeight + fixedNormalHeight) ;
if(tableViewOffSetY <= (fixedNormalHeight-fixedSmallHeight)) {
CGRect frame = _fixedView.frame ;
CGFloat height = fixedNormalHeight - tableViewOffSetY ;
frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, height) ;
_fixedView.frame = frame ;
}
else {
_fixedView.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, fixedSmallHeight) ;
}
}
else {
_movedView.frame = CGRectMake(0, tableViewOffSetY, [UIScreen mainScreen].bounds.size.width, movedHeight + fixedNormalHeight) ;
_fixedView.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, fixedNormalHeight) ;
if(tableViewOffSetY<-refreshHeight-10 && [scrollView isDecelerating]) {
[self requird] ;
[self.tableView setContentOffset:CGPointMake(0, -refreshHeight) animated:YES] ;
}
}
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
if(!decelerate) {
if(scrollView.contentOffset.y >= 0) {
if(scrollView.contentOffset.y < fixedSmallHeight*0.5) {
[scrollView setContentOffset:CGPointMake(0, 0) animated:YES] ;
}
else if(scrollView.contentOffset.y < fixedSmallHeight) {
[scrollView setContentOffset:CGPointMake(0, fixedSmallHeight) animated:YES] ;
}
}
}
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
if(scrollView.contentOffset.y >= 0) {
if(scrollView.contentOffset.y < fixedSmallHeight*0.5) {
[scrollView setContentOffset:CGPointMake(0, 0) animated:YES] ;
}
else if(scrollView.contentOffset.y < fixedSmallHeight) {
[scrollView setContentOffset:CGPointMake(0, fixedSmallHeight) animated:YES] ;
}
}
}
代碼中,下拉超過一定距離就進(jìn)行更新請求睛藻,并在請求期間顯示請求模塊
根據(jù)第二輪分析启上,整個(gè)頁面的實(shí)現(xiàn)就會(huì)變得非常簡單。
代碼實(shí)現(xiàn)
具體的實(shí)現(xiàn)代碼見:
https://github.com/bbbxxxbx/-