最近項目中需要做一個兩個
tableVIew
聯動的效果,有點類似于餓了嗎外賣點餐的那種效果,雖然實現起來難度不是太大,但是還是記錄下來,方便有需要的開發者少走一些彎路.個人認為做什么效果主要還是一個思路問題,思路對了,做起來也就得心應手了.廢話不多說,先附上效果圖.
具體實現步驟
-
1 . 首先確定好思路,底層是用
viewController
, 上面的地址和派送次數的視圖是用兩個View
做的,下面兩個tableView
,左邊是leftTableView
, 右邊是rightTableView
.這樣做的目的是為了tableView
滑動到頂部的時候整個視圖有個上移的動畫效果,這樣做更加方便一點.
圖1.png 2.接下來創建2個
view
和leftTableView
,rightTableView
遵循代理,加載數據源.進入頁面默認選中leftTableView
第一個單元格,這些步驟忽略.
默認選中第一個單元格
[self.leftTableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] animated:YES scrollPosition:UITableViewScrollPositionNone];
- 3.點擊左側
tableView
的時候,讓右側的tableView
滾動到指定的分區.實現tableView
的代理方法.取出當前的分區的indexpatg.row
就是rightTbaleView
的分區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) {
// 不打對勾
leftCell.titleLabel.textColor = [UIColor blackColor];
}
// 打對勾
leftCell.titleLabel.textColor = kOrangeTextColor;
[_rightTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:indexPath.row] atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
}
- 4.滾動
rightTableView
的時候讓左側tableView
滾動到指定的行,也就是rightTableView
的分區section
,和leftTableView
的indexPath.row
是相對應的.實現scrollView
的代理方法.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
// 滑動視圖上移動畫
[self viewUpAnimationWihtScrollView:scrollView];
if (scrollView == self.rightTableView) {
//取出當前顯示的最頂部的cell的indexpath
//當前tableview頁面可見的分區屬性 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.當滑動
tableView
的時候添加動畫,tableView
向上滑動的時候讓View
向上偏移.這一步在scrollViewDidScroll:(UIScrollView *)scrollView
中調用.
- (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;
}];
}
}
這樣就實現了兩個
tableView
聯動的效果,其中有2個方法比較關鍵:
- -1 .點擊
leftTableView
的時候讓rightTableView
滾動到指定分區.
[_rightTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:indexPath.row] atScrollPosition:UITableViewScrollPositionTop animated:YES];
- -2 .
rightTableView
滾動的時候,獲取當前頁面可見的分區行數,
NSIndexPath *topIndexPath = [[self.rightTableView indexPathsForVisibleRows]firstObject];
優化處理
--- 根據賣香蕉的大叔的建議,對tableView
單選處理進行優化.以前我寫的那種方式是用NSArray *array = [tableView visibleCells];
方法遍歷出所有的單元格,然后重新改變title
的顏色和cell
的背景顏色,然后給當前點擊的這個單元格重新改變新的顏色,這樣的操作不利于tableView
的優化,如果cell
足夠多的時候,這樣做會造成界面卡頓.
- 1.把最后點擊的最后點擊的
cell
的indexPath
定義成屬性.
@property(nonatomic, strong) NSIndexPath *lastPath; // 單選
- 2.在點擊
cell
的時候記錄下最新的indexPath
,并且把最新點擊的cell
上的title
和背景顏色進行修改,把以前的cell
再更改回原始狀態.
- (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.為了防止單元格重用出現問題,需要在
cellForRowAtIndexPath
中判斷當前cell
的選中狀態.
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) {
// 被選中狀態
leftCell.contentView.backgroundColor = KLightYellowColor;
leftCell.titleLabel.textColor = kOrangeTextColor;
}else{
leftCell.contentView.backgroundColor = [UIColor clearColor];
leftCell.titleLabel.textColor = [UIColor blackColor];
}
這種處理適用于自定義單元格的單選處理,在這里也正好記錄下來,方便給需要用到此功能的開發者多一些思路,再次感謝賣香蕉的大叔給出的意見.
--- 感謝TryToFlyHigher提出的bug,這個bug是由于點擊左側leftTableView
讓右側rightTableView
滾動的時候,導致左側的leftTableView
又重新選中了一次,就像選中框閃了一下的感覺.目前這個bug已經解決,主要思路是定義一個BOOL值表示是否重復滾動,在左側leftTableView
選中的時候把BOOL值置為YES,在scrollView
即將拖動的時候置為NO,在- (void)scrollViewDidScroll:(UIScrollView *)scrollView
方法中加上判斷,是否重復滾動.
- 1.定義BOOL值表示是否重復滾動,并給默認值為NO
@property (nonatomic, assign) BOOL isRepeatRolling; // 是否重復滾動
self.isRepeatRolling = NO; // 默認NO
- 在
tableView
滾動的時候把值置為YES.表示重復選中了,并在scrollViewDidScroll:(UIScrollView *)scrollView
添加判斷是否重復滾動
- 在
if (self.isRepeatRolling == NO) { // 防止重復滾動
[self.leftTableView selectRowAtIndexPath:moveIndexPath animated:NO scrollPosition:UITableViewScrollPositionMiddle];
}
- 3.在
scrollView
開始拖動的時候更改BOOL值為NO
// scrollView 開始拖動
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
self.isRepeatRolling = NO;
}
趁著這次打開項目解決掉這個bug
,順便適配了一下iOS11
.
gitHub已經更新,再次感謝TryToFlyHigher提出的bug.
結尾
在項目中使用了MJExtension
字典轉模型,和masonry
屏幕適配.
至此就大概實現了列表聯動的效果,可以實現的方法有很多,在這里只是提出一個思路,讓開發者能夠少走一些彎路.
另附本文gitHub地址 ,喜歡的給個星星哦.非常感謝..