最近項目中需要做一個兩個
tableVIew
聯(lián)動的效果,有點類似于餓了嗎外賣點餐的那種效果,雖然實現(xiàn)起來難度不是太大,但是還是記錄下來,方便有需要的開發(fā)者少走一些彎路.個人認(rèn)為做什么效果主要還是一個思路問題,思路對了,做起來也就得心應(yīng)手了.廢話不多說,先附上效果圖.
具體實現(xiàn)步驟
-
1 . 首先確定好思路,底層是用
viewController
, 上面的地址和派送次數(shù)的視圖是用兩個View
做的,下面兩個tableView
,左邊是leftTableView
, 右邊是rightTableView
.這樣做的目的是為了tableView
滑動到頂部的時候整個視圖有個上移的動畫效果,這樣做更加方便一點.
圖1.png 2.接下來創(chuàng)建2個
view
和leftTableView
,rightTableView
遵循代理,加載數(shù)據(jù)源.進(jìn)入頁面默認(rèn)選中leftTableView
第一個單元格,這些步驟忽略.
默認(rèn)選中第一個單元格
[self.leftTableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] animated:YES scrollPosition:UITableViewScrollPositionNone];
- 3.點擊左側(cè)
tableView
的時候,讓右側(cè)的tableView
滾動到指定的分區(qū).實現(xiàn)tableView
的代理方法.取出當(dāng)前的分區(qū)的indexpatg.row
就是rightTbaleView
的分區(qū)section
.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath: (NSIndexPath *)indexPath {
if (_leftTableView == tableView) {
AYLeftPickTableViewCell *leftCell = [tableView cellForRowAtIndexPath:indexPath];
leftCell.contentView.backgroundColor = KLightYellowColor;
NSArray *array = [tableView visibleCells];
for (AYLeftPickTableViewCell *leftCell in array) {
// 不打?qū)? leftCell.titleLabel.textColor = [UIColor blackColor];
}
// 打?qū)? leftCell.titleLabel.textColor = kOrangeTextColor;
[_rightTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:indexPath.row] atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
}
- 4.滾動
rightTableView
的時候讓左側(cè)tableView
滾動到指定的行,也就是rightTableView
的分區(qū)section
,和leftTableView
的indexPath.row
是相對應(yīng)的.實現(xiàn)scrollView
的代理方法.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
// 滑動視圖上移動畫
[self viewUpAnimationWihtScrollView:scrollView];
if (scrollView == self.rightTableView) {
//取出當(dāng)前顯示的最頂部的cell的indexpath
//當(dāng)前tableview頁面可見的分區(qū)屬性 indexPathsForVisibleRows
// 取出顯示在 視圖 且最靠上 的 cell 的 indexPath
// 判斷tableView是否滑動到最底部
CGFloat height = scrollView.frame.size.height;
CGFloat contentOffsetY = scrollView.contentOffset.y;
CGFloat bottomOffset = scrollView.contentSize.height - contentOffsetY;
if (bottomOffset <= height) {
NSIndexPath *bottomIndexPath = [[self.rightTableView indexPathsForVisibleRows] lastObject];
NSIndexPath *moveIndexPath = [NSIndexPath indexPathForRow:bottomIndexPath.section inSection:0];
[self.leftTableView selectRowAtIndexPath:moveIndexPath animated:NO scrollPosition:UITableViewScrollPositionMiddle];
} else {
NSIndexPath *topIndexPath = [[self.rightTableView indexPathsForVisibleRows]firstObject];
NSIndexPath *moveIndexPath = [NSIndexPath indexPathForRow:topIndexPath.section inSection:0];
[self.leftTableView selectRowAtIndexPath:moveIndexPath animated:NO scrollPosition:UITableViewScrollPositionMiddle];
}
}else{
return;
}
}
- 5.當(dāng)滑動
tableView
的時候添加動畫,tableView
向上滑動的時候讓View
向上偏移.這一步在scrollViewDidScroll:(UIScrollView *)scrollView
中調(diào)用.
- (void)viewUpAnimationWihtScrollView:(UIScrollView *)scrollView {
if (scrollView.contentOffset.y > 0) {
[self.addressView mas_updateConstraints:^(MASConstraintMaker *make) {
make.left.right.mas_equalTo(0);
make.top.mas_equalTo(-Address_Height+64);
make.height.mas_equalTo(Address_Height);
}];
[UIView animateWithDuration:0.2 animations:^{
[self.view layoutIfNeeded];
self.rightTableView.frame = CGRectMake(LeftTable_Width, SendView_Height+64, kScreenWidth - LeftTable_Width, kScreenHeight - 64 - 50 - SendView_Height);
self.leftTableView.frame = CGRectMake(0, SendView_Height+64, LeftTable_Width, kScreenHeight - 64 - 50 - SendView_Height);
} completion:^(BOOL finished) {
nil;
}];
} else {
[self.addressView mas_updateConstraints:^(MASConstraintMaker *make) {
make.left.right.mas_equalTo(0);
make.top.mas_equalTo(64);
make.height.mas_equalTo(Address_Height);
}];
[UIView animateWithDuration:0.2 animations:^{
[self.view layoutIfNeeded];
self.rightTableView.frame = CGRectMake(LeftTable_Width, Address_Height + SendView_Height+64, kScreenWidth - LeftTable_Width, kScreenHeight - 64 - 50 - Address_Height - SendView_Height);
self.leftTableView.frame = CGRectMake(0, Address_Height + SendView_Height+64, LeftTable_Width, kScreenHeight - 64 - 50 - Address_Height - SendView_Height);
} completion:^(BOOL finished) {
nil;
}];
}
}
這樣就實現(xiàn)了兩個
tableView
聯(lián)動的效果,其中有2個方法比較關(guān)鍵:
- -1 .點擊
leftTableView
的時候讓rightTableView
滾動到指定分區(qū).
[_rightTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:indexPath.row] atScrollPosition:UITableViewScrollPositionTop animated:YES];
- -2 .
rightTableView
滾動的時候,獲取當(dāng)前頁面可見的分區(qū)行數(shù),
NSIndexPath *topIndexPath = [[self.rightTableView indexPathsForVisibleRows]firstObject];
優(yōu)化處理
--- 根據(jù)賣香蕉的大叔的建議,對tableView
單選處理進(jìn)行優(yōu)化.以前我寫的那種方式是用NSArray *array = [tableView visibleCells];
方法遍歷出所有的單元格,然后重新改變title
的顏色和cell
的背景顏色,然后給當(dāng)前點擊的這個單元格重新改變新的顏色,這樣的操作不利于tableView
的優(yōu)化,如果cell
足夠多的時候,這樣做會造成界面卡頓.
- 1.把最后點擊的最后點擊的
cell
的indexPath
定義成屬性.
@property(nonatomic, strong) NSIndexPath *lastPath; // 單選
- 2.在點擊
cell
的時候記錄下最新的indexPath
,并且把最新點擊的cell
上的title
和背景顏色進(jìn)行修改,把以前的cell
再更改回原始狀態(tài).
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (_leftTableView == tableView) {
NSInteger newRow = [indexPath row];
NSInteger oldRow = (self .lastPath !=nil)?[self .lastPath row]:-1;
if (newRow != oldRow) {
AYLeftPickTableViewCell *newCell = [tableView cellForRowAtIndexPath:indexPath];
newCell.contentView.backgroundColor = KLightYellowColor;
newCell.titleLabel.textColor = kOrangeTextColor;
AYLeftPickTableViewCell *oldCell = [tableView cellForRowAtIndexPath:self.lastPath];
oldCell.contentView.backgroundColor = [UIColor clearColor];
oldCell.titleLabel.textColor = [UIColor blackColor];
}
self.lastPath = indexPath;
[_rightTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:indexPath.row] atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
}
- 3.為了防止單元格重用出現(xiàn)問題,需要在
cellForRowAtIndexPath
中判斷當(dāng)前cell
的選中狀態(tài).
AYLeftPickTableViewCell *leftCell = [tableView dequeueReusableCellWithIdentifier:@"AYLeftPickTableViewCell" forIndexPath:indexPath];
AYCollectFoodModel *model = self.dataArray[indexPath.row];
leftCell.titleLabel.text = model.vegname;
leftCell.selectionStyle = UITableViewCellSelectionStyleNone;
NSInteger row = [indexPath row];
NSInteger oldRow = [_lastPath row];
if (row == oldRow && _lastPath!=nil) {
// 被選中狀態(tài)
leftCell.contentView.backgroundColor = KLightYellowColor;
leftCell.titleLabel.textColor = kOrangeTextColor;
}else{
leftCell.contentView.backgroundColor = [UIColor clearColor];
leftCell.titleLabel.textColor = [UIColor blackColor];
}
這種處理適用于自定義單元格的單選處理,在這里也正好記錄下來,方便給需要用到此功能的開發(fā)者多一些思路,再次感謝賣香蕉的大叔給出的意見.
--- 感謝TryToFlyHigher提出的bug,這個bug是由于點擊左側(cè)leftTableView
讓右側(cè)rightTableView
滾動的時候,導(dǎo)致左側(cè)的leftTableView
又重新選中了一次,就像選中框閃了一下的感覺.目前這個bug已經(jīng)解決,主要思路是定義一個BOOL值表示是否重復(fù)滾動,在左側(cè)leftTableView
選中的時候把BOOL值置為YES,在scrollView
即將拖動的時候置為NO,在- (void)scrollViewDidScroll:(UIScrollView *)scrollView
方法中加上判斷,是否重復(fù)滾動.
- 1.定義BOOL值表示是否重復(fù)滾動,并給默認(rèn)值為NO
@property (nonatomic, assign) BOOL isRepeatRolling; // 是否重復(fù)滾動
self.isRepeatRolling = NO; // 默認(rèn)NO
- 在
tableView
滾動的時候把值置為YES.表示重復(fù)選中了,并在scrollViewDidScroll:(UIScrollView *)scrollView
添加判斷是否重復(fù)滾動
- 在
if (self.isRepeatRolling == NO) { // 防止重復(fù)滾動
[self.leftTableView selectRowAtIndexPath:moveIndexPath animated:NO scrollPosition:UITableViewScrollPositionMiddle];
}
- 3.在
scrollView
開始拖動的時候更改BOOL值為NO
// scrollView 開始拖動
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
self.isRepeatRolling = NO;
}
趁著這次打開項目解決掉這個bug
,順便適配了一下iOS11
.
gitHub已經(jīng)更新,再次感謝TryToFlyHigher提出的bug.
結(jié)尾
在項目中使用了MJExtension
字典轉(zhuǎn)模型,和masonry
屏幕適配.
至此就大概實現(xiàn)了列表聯(lián)動的效果,可以實現(xiàn)的方法有很多,在這里只是提出一個思路,讓開發(fā)者能夠少走一些彎路.
另附本文gitHub地址 ,喜歡的給個星星哦.非常感謝..