外卖 App 双列表联动 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
iOS 开发实用技术导航
NSHipster 中文版
http://nshipster.cn/
cocos2d 开源 2D 游戏引擎
http://www.cocos2d-iphone.org/
CocoaPods
http://cocoapods.org/
Google Analytics for Mobile 统计解决方案
http://code.google.com/mobile/analytics/
WWDC
https://developer.apple.com/wwdc/
Design Guides and Resources
https://developer.apple.com/design/
Transcripts of WWDC sessions
http://asciiwwdc.com
Cocoa with Love
http://cocoawithlove.com/
Cocoa Dev Central
http://cocoadevcentral.com/
NSHipster
http://nshipster.com/
Style Guides
Google Objective-C Style Guide
NYTimes Objective-C Style Guide
Useful Tools and Services
Charles Web Debugging Proxy
Smore
stephenliubp
V2EX    iDev

外卖 App 双列表联动

  •  
  •   stephenliubp
    FantasticLBP 2017-09-24 16:11:36 +08:00 3321 次点击
    这是一个创建于 3015 天前的主题,其中的信息可能已经有所发展或是发生改变。

    双列表联动

    用过了那么多的外卖 App,总结出一个规律,那就是“所有的外卖 App 都有双列表联动功能”。哈哈哈哈,这是一个玩笑。

    这次我也需要开发具有联动效果的双列表。也是首次开发这种类型的 UI,记录下步骤与心得

    一、关键思路

    • 懒加载左右 2 个 UITableView
    • 根据需要自定义 Cell
    • 2 个 UITableView 加载到界面上的时候注意下部剧就好
    • 因为需要联动效果,所有左侧的 UITableView 一般是大的分类,右边的 UITableView 一般是大分类小的小分类,所以有了这样的特点
      • 左边的 UITableView 是只有 1 个 section 和 n 个 row
      • 右边的 UITableView 具有 n 个 section (这里的 section 个数恰好是左边 UITableView 的 row 数量),且每个 section 下的 row 由对应的数据源控制

    二、第一版代码

    #pragma mark -- UITableViewDelegate -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{ if (tableView == self.leftTablview) { return 1; } return self.datas.count; } -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ if (tableView == self.leftTablview) { return self.datas.count; } QuestionCollectionModel *model = self.datas[section]; NSArray *questiOns=model.questions; return questions.count; } -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{ if (tableView == self.leftTablview) { return LeftCellHeight; } return RightCellHeight; } -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ if (tableView == self.leftTablview) { PregnancyPeriodCell *cell = [tableView dequeueReusableCellWithIdentifier:PregnancyPeriodCellID forIndexPath:indexPath]; if (self.collectiOnType== CollectionType_Wrong || self.collectiOnType== CollectionType_Miss) { QuestionCollectionModel *model = self.datas[indexPath.row]; cell.week = model.tag; } return cell; } QuestionCell *cell = [tableView dequeueReusableCellWithIdentifier:QuestionCellID forIndexPath:indexPath]; QuestionCollectionModel *model = self.datas[indexPath.section]; NSArray *questiOns=model.questions; QuestionModel *questiOnModel= questions[indexPath.row]; cell.model = questionModel; return cell; } -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ if (tableView == self.leftTablview) { NSIndexPath *indexpath = [NSIndexPath indexPathForRow:0 inSection:indexPath.row]; [self.rightTableview scrollToRowAtIndexPath:indexpath atScrollPosition:UITableViewScrollPositionTop animated:YES]; } } -(void)scrollViewDidScroll:(UIScrollView *)scrollView{ if (scrollView == self.rightTableview) { NSIndexPath *indexpath = [self.rightTableview indexPathsForVisibleRows].firstObject; NSIndexPath *leftScrollIndexpath = [NSIndexPath indexPathForRow:indexpath.section inSection:0]; [self.leftTablview selectRowAtIndexPath:leftScrollIndexpath animated:YES scrollPosition:UITableViewScrollPositionMiddle]; } } 

    缺陷:虽然实现了效果,但是有缺陷。点击左侧的 UITableView,右侧的 UITableViewe 滚动到相应的位置,这是没问题的,但是滚动

    右边,需要根据右边 indexPath.section 将选中左侧相应的 indexPath。这样左侧选中的时候,又会触发右边滚动的事件,整体看上去不是很流畅。

    三、解决方案

    观察了下,发现右侧滚动的时候左侧会上下选中,所以也就是只要让右侧滚动的时候,左侧的 UITableView 单方向选中,不要滚动就好,所以由于 UITableView 也是 UIScrollview,所以在 scrollViewDidScroll 方法中判断右侧的 UITableView 是向上还是向下滚动,以此作为判断条件来让左侧的 UITableView 选中相应的行。

    且之前是在 scrollview 代理方法中让左侧的 tableview 选中,这样子又会触发左侧 tableview 的选中事件,从而导致右侧的 tablview 滚动,造成不严谨的联动逻辑

    改进后的方法:

    1. 点击左侧的 UITableView,在代理方法 didSelectRowAtIndexPath 中拿到相应的 indexPath.row ,计算出右侧 UITableView 需要滚动的 indexPath 的位置。
       [self.rightTableview scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:indexPath.row] atScrollPosition:UITableViewScrollPositionTop animated:YES]; 
    2. 在 willDisplayCell 和 didEndDisplayingCell 代理方法中选中左侧 UITableView 相应的行。
    -(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath{ if (tableView == self.rightTableview && !self.isScrollDown && self.rightTableview.isDragging ) { [self.leftTablview selectRowAtIndexPath:[NSIndexPath indexPathForRow:indexPath.section inSection:0] animated:YES scrollPosition:UITableViewScrollPositionTop]; } } -(void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath{ if (tableView == self.rightTableview && self.isScrollDown && self.rightTableview.isDragging) { [sel.leftTablview selectRowAtIndexPath:[NSIndexPath indexPathForRow:indexPath.section+1 inSection:0] animated:YES scrollPosition:UITableViewScrollPositionTop]; } } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(nonnull NSIndexPath *)indexPath { if (self.leftTablview == tableView) { [self.rightTableview scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:indexPath.row] atScrollPosition:UITableViewScrollPositionTop animated:YES]; }else{ NSLog(@"嗡嗡嗡"); } } #pragma mark - UIScrollViewDelegate - (void)scrollViewDidScroll:(UIScrollView *)scrollView{ static CGFloat lastOffsetY = 0; UITableView *tableView = (UITableView *)scrollView; if (self.rightTableview == tableView){ self.isScrollDown = (lastOffsetY < scrollView.contentOffset.y); lastOffsetY = scrollView.contentOffset.y; } } 
    效果图

    效果图

    附上 Demo:Demo

    目前尚无回复
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2523 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 26ms UTC 15:09 PVG 23:09 LAX 07:09 JFK 10:09
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86