tableView列表联动
示例.gif最近项目中需要做一个两个
tableVIew
联动的效果,有点类似于饿了吗外卖点餐的那种效果,虽然实现起来难度不是太大,但是还是记录下来,方便有需要的开发者少走一些弯路.个人认为做什么效果主要还是一个思路问题,思路对了,做起来也就得心应手了.废话不多说,先附上效果图.
具体实现步骤
-
1 . 首先确定好思路,底层是用
图1.pngviewController
, 上面的地址和派送次数的视图是用两个View
做的,下面两个tableView
,左边是leftTableView
, 右边是rightTableView
.这样做的目的是为了tableView
滑动到顶部的时候整个视图有个上移的动画效果,这样做更加方便一点.
-
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地址 ,喜欢的给个星星哦.非常感谢..