应用情景

情景一:

情景一

说明:是不是和tableView的Plain类型一样,其实这个是由两个列表实现的

情景二:

情况二

说明:此时,就可以发现和普通的列表有些不一样了

情景三:

情景三

说明:笔者最初就是为了实现这种情况,由于项目需求,需要防QQ空间,不同的是需要类型的切换,当时没想到好的解决方案,最后受同事启发,在其demo上进行修改,使得tableView可以满足大部分的悬停需求

思路说明

1、由于是两个tableView嵌套实现,所以首要就是兼容手势,使得我们的拖拽手势可以向下传递

2、通过改变列表的contentOffset来让列表是否"滚动"

3、子列表默认不滚动,当父列表滚动到需要悬停的位置时,父列表"停止"滚动,子列表开始滚动

4、当子列表下拉,contentOffset.y小于0时,子列表"停止滚动",父列表开始"滚动"

5、使用通知来接收滚动消息

主要实现过程(代码)

首先继承UITableView新建LolitaTableView类,并定义三种类型

typedef NS_ENUM(NSInteger , LLNestedTableViewType) {

LLNestedTableViewTypeNormal, //该类型和 UITableView 一致,未做其他设置

LLNestedTableViewTypeMain, //主列表的类型

LLNestedTableViewTypeSub //子列表的类型

};

1、兼容手势

/// 向下传递手势,触发主列表的滚动

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{

if (self.typeNested == LLNestedTableViewTypeMain) { // 主table类型的需要兼容手势

return YES;

}

return NO;

}

2、注册通知

// 监听列表滚动的通知

[NSNotificationCenter.defaultCenter addObserverForName:LLNestedTableViewStopNotification object:nil queue:NSOperationQueue.mainQueue usingBlock:^(NSNotification * _Nonnull note) {

// 这里触发手动控制主从列表的滚动与否

}];

3、重写setContentOffset方法

/// 重写,参与滚动事件

-(void)setContentOffset:(CGPoint)contentOffset{

[super setContentOffset:contentOffset];

if (self.typeNested == LLNestedTableViewTypeNormal) {

return; // 普通类型不做修改

}

CGFloat y = contentOffset.y;

switch (self.typeNested) {

// 主列表类型

case LLNestedTableViewTypeMain:

{

CGFloat stayPosition = 0;

// 获取到停留的位置

if ([self.delegateNested respondsToSelector:@selector(llNestedTableViewStayPosition:)]) {

stayPosition = [self.delegateNested llNestedTableViewStayPosition:self];

}

if (self.canScroll) {

// 当主列表滚动位置超过预设时,我们发出通知,让子列表不能滚动

if (y > stayPosition) {

contentOffset.y = stayPosition;

[super setContentOffset:contentOffset];

self.canScroll = NO;

[NSNotificationCenter.defaultCenter postNotificationName:LLNestedTableViewStopNotification object:self];

} else {

[super setContentOffset:contentOffset];

}

} else {

contentOffset.y = stayPosition; // 让其“停止”在预设位置,取消动画,否则会因为时间差一直循环

[super setContentOffset:contentOffset animated:NO];

}

}

break;

// 子列表类型

case LLNestedTableViewTypeSub:

{

if (self.canScroll) {

// 当子列表被下拉到最初位置时,我们让其“停止”,并发送通知,让主列表可以滚动

if (y < 0) {

[super setContentOffset:CGPointZero];

self.canScroll = NO;

[NSNotificationCenter.defaultCenter postNotificationName:LLNestedTableViewStopNotification object:self];

} else {

[super setContentOffset:contentOffset];

}

} else {

[super setContentOffset:CGPointZero];

}

}

break;

default:

break;

}

}

4、通知处理

// 这里触发手动控制主从列表的滚动与否

LLNestedTableView* table = note.object;

if (self.typeNested == LLNestedTableViewTypeNormal ||

![table isKindOfClass:UITableView.class]||

(self.flag.length && ![self.flag isEqualToString:table.flag]))

{ return; }

// 当发送通知方和当前对象不一致,则表示当前对象需要开启滚动

if (self != table) { self.canScroll = YES; }

// 把其他所有的sub都移动到顶部,除去主的,其他table皆不能滚动

if (table.typeNested == LLNestedTableViewTypeSub && self.typeNested == LLNestedTableViewTypeSub) {

[self setContentOffset:CGPointZero];

self.canScroll = NO;

}

使用

1、父类tableView

@property (strong ,nonatomic) LLNestedTableView *mainTable;

// 初始化父类列表

-(LLNestedTableView *)mainTable{

if (_mainTable==nil) {

_mainTable = [[LLNestedTableView alloc] initWithFrame:CGRectMake(0, 64, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height-64) style:UITableViewStyleGrouped];

_mainTable.delegate = self;

_mainTable.dataSource = self;

_mainTable.delegateNested = self; // 悬停代理

_mainTable.tableFooterView = [UIView new];

_mainTable.showsVerticalScrollIndicator = NO;

_mainTable.typeNested = LLNestedTableViewTypeMain; // 列表类型

}

return _mainTable;

}

注:需要将子列表加到列表上,最好是最后一个section的cell上,这样比较灵活;其他位置也可以添加,主要是需要配合悬停的位置使用

// !!!: 悬停的位置

- (CGFloat)llNestedTableViewStayPosition:(LLNestedTableView *)tableView{

return tableView.tableHeaderView.frame.size.height;

}

2、子类列表

-(LLNestedTableView *)table{

if (_table==nil) {

_table = [[LLNestedTableView alloc] initWithFrame:CGRectZero];

_table.delegate = self;

_table.dataSource = self;

_table.showsVerticalScrollIndicator = NO;

_table.tableFooterView = [UIView new];

_table.typeNested = LLNestedTableViewTypeSub; // 除了类型要设置为子类,用法和系统类型一样

}

return _table;

}

2019-12-16 补充:

支持了 Cocoapods ,方便集成 : pod 'LLNestedTableView';

修复了 iOS 13 下滚动异常的问题;

新增了联动的标识,防止多个页面错误通知;

新增了 KVO 的形式进行联动。

2020-07-27 补充:

重构了 LLNestedTableView,支持 列表视图 + 集合视图 的联动。

tableView_CollectionView.gif

注意:

1、悬停位置的设置改用回调的方式实现

2、如果你的内容视图非上述两个视图,如 UIScrollView,请自行转换成上述两个视图,或者根据 列表视图 或者 集合视图实现的方式自行实现,大体思路不变

3、关于内容视图 和 主列表 同时滚动的问题,目前解决方案是,手动禁止某些滚动视图的滚动事件。PS:另一种思路是改写关键方法 -gestureRecognizer: shouldRecognizeSimultaneouslyWithGestureRecognizer: 的值,笔者目前还没有踩坑,只提供一种思路

4、UITableView/UICollectionView 需要弹性效果,UITableView 竖直方向默认是开启的,UICollectionView 在数量少,内容视图小于可见视图时,弹性是关闭的,你需要设置 alwaysBounceVertical 为 YES/true

uitableview 弹性_iOS UITableView/UICollectionView的嵌套和悬停的解决方案相关推荐

  1. uitableview 弹性_iOS UITableView滚动头图 拉伸放大效果 (头部弹性效果) 增加iOS11支持 附有demo...

    今天修改日期为2017年11月25日 两个月前做了iOS11的bug修复,才对博客进行更新,见谅. 在iOS11上需要注意两个问题 1.使用UIScrollview,UITableView,UIWeb ...

  2. ios wallet开发_iOS: 使用UICollectionView实现Wallet效果

    Wallet的拖拉效果是不是很炫酷,笔者仿照着自己写了一个Demo, 效果还是可以滴!为什么要用CollectionView来写呢,因为我们可以自定义cell的layout attributes,如果 ...

  3. 一种嵌套滑动冲突的解决方案

    非嵌套滑动 | 嵌套滑动 相比起非嵌套滑动的自定义分发事件的方案,嵌套滑动冲突有比较成熟的 Google 解决方案:NestedScrolling . 三层嵌套的滑动冲突 UI 层级如下: 最外层(底 ...

  4. ios wkwebview弹框_iOS WKWebView的javascript alert 不弹的解决方案

    1.将WKWebView的WKUIDelegate设置成self. 2.将一下三个方法拷贝到项目中. - (void)webView:(WKWebView *)webView runJavaScrip ...

  5. iframe嵌套的html高度,iframe 多层嵌套 无限嵌套 高度自适应的解决方案

    有A,B,C三个页面,A页面包含B页面,B页面包含C页面.A页面随着B页面自适应,C页面随着B页面自适应 A页面 复制代码代码如下: οnlοad="this.height=main.doc ...

  6. 苹果侧边滑动返回_iOS系统右滑返回手势问题及解决方案

    在iOS7之后,苹果推出了手势滑动返回功能,也就是从屏幕左侧向右滑动可返回上一个界面.大大提高了APP在大屏手机和iPad上的操作体验,场景切换更加流畅. 常见的问题有: 1.右滑手势失效 2.右滑手 ...

  7. UICollectionView详解和UITableView的区别

    1. UICollectionView 和 UITableView 的UI区别 UICollectionView默认没有表头,  UITableView: 有表头和表尾; UICollectionVi ...

  8. UICollectionView和UITableView的区别

    原文:https://blog.csdn.net/vbirdbest/article/details/50720915 1. UICollectionView 和 UITableView 的UI区别 ...

  9. iOS 和 Android:UITableView与RecycleView的重用机制比较

    引言:iOS和Android各有自己的列表组件.众所周知,列表组件一直都是移动端各个端中,组件重用.内存优化的重点.今天就来分析下iOS和Android各自的重用机制. Android:Recycle ...

最新文章

  1. JavaScript 的面向对象编程
  2. java cordova_java – Cordova android后台插件在5分钟后被杀死
  3. 20个堪称神器的命令行软件
  4. python2.7下面字节数组(ByteArray)和16进制字符串(HexString)转化
  5. android 语音通信,Android与PC间的语音通信
  6. Floodlight 在 ChannelPipeline 图
  7. (转)C结构体之位域(位段)
  8. 第一次冲刺-团队开发(第六天)
  9. 不用空格怎么打两个空格_为什么在寸土寸金的键盘上,空格键却要做这么长,究竟怎么回事?...
  10. 初学云计算:华为vs阿里vs红帽,如何选择?
  11. 图书速读 | 一分钟读完《斯坦福高效睡眠法》
  12. 删除PDF其中几页的方法
  13. 天池比赛——新闻文本分类比赛(零基础入门NLP)
  14. 【机器学习之向量求导】分子布局 分母布局
  15. 腾讯运维专家的自我修养
  16. 网站数据统计分析之二:前端日志采集是与非
  17. redhat开启telnet后无法连接
  18. 【雷神笔记本快捷键】雷神笔记本FN功能快捷键大全以及电脑CPU处于低功耗但电脑风扇高速转动噪音较大解决方案
  19. 物联网卡是否正规卡,有什么功能?
  20. flv,wmv等视频格式加速播放方法

热门文章

  1. 基于 MongoDB 动态字段设计的探索 (二) 聚合操作
  2. 吴恩达机器学习week2
  3. POJ 2135 Farm Tour (最小费用最大流)
  4. 配置cocos2d-js 开发环境 通过CMD 创建工程
  5. 长沙理工 ACM 数位 DP 1488
  6. 我的程序员之路(4)——工作半年
  7. 通过MVC模式将Web视图和逻辑代码分离
  8. AndroidStudio安卓原生开发_android按钮防止短时间内连续点击_在android_java中都能使用---Android原生开发工作笔记131
  9. MFC工作笔记0004---MFC中afx_msg是什么,afx_msg void function()是什么意思
  10. STM32工作笔记004---了解高速版PCB设计Cadence