{"id":19134995,"url":"https://github.com/bluematthew/pagingsublist","last_synced_at":"2026-06-08T17:30:13.012Z","repository":{"id":113072083,"uuid":"271594837","full_name":"BlueMatthew/PagingSubList","owner":"BlueMatthew","description":"Pagination On UICollectionView","archived":false,"fork":false,"pushed_at":"2020-08-20T13:15:19.000Z","size":461,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-01-03T12:27:40.732Z","etag":null,"topics":["nested","pagination","uicollectionview","uiscrollview"],"latest_commit_sha":null,"homepage":"","language":"Objective-C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/BlueMatthew.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-06-11T16:27:56.000Z","updated_at":"2022-01-20T16:39:57.000Z","dependencies_parsed_at":null,"dependency_job_id":"208418f0-f21b-4a91-8200-baa00b5c8885","html_url":"https://github.com/BlueMatthew/PagingSubList","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BlueMatthew%2FPagingSubList","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BlueMatthew%2FPagingSubList/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BlueMatthew%2FPagingSubList/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BlueMatthew%2FPagingSubList/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/BlueMatthew","download_url":"https://codeload.github.com/BlueMatthew/PagingSubList/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240214611,"owners_count":19766263,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["nested","pagination","uicollectionview","uiscrollview"],"created_at":"2024-11-09T06:28:50.622Z","updated_at":"2026-06-08T17:30:12.962Z","avatar_url":"https://github.com/BlueMatthew.png","language":"Objective-C","funding_links":[],"categories":[],"sub_categories":[],"readme":"## 列表中内嵌多个子列表的横向分页滚动\n\n一个列表在展示一些内容之后，出现多个类目的分支控制，即子列表，子列表跟随类目的选择而切换，是APP常见的场景。而在子列表上通过左右滑动进行类目的切换，也是一种流行的做法，左右滑动切换子列表的需求给实现带了来了额外的难题。\n\n![DemoGIF](https://github.com/BlueMatthew/PagingSubList/raw/master/docs/res/paging.gif)\n\n网络上比较多的解法是父列表中嵌套一个PageScrollView，在这个PageScrollView中放入多个子列表，通过PageScrollView的分页能力实现左右滑动。这种方案需要解决内外两个列表的滑动的连贯性问题，即嵌套ScrollView的手势传递(一般来说，列表也是一种ScrollView)。实践下来，一方面，滑动手势传递在某些特定场景下还存在着一些问题，无法做到尽善尽美；另一方面，处理手势传递的实现需要和ScrollView的多个回调都产生关联，代码非常琐碎离散，耦合度很高，不能很好的封装和复用；最后，这种方案需要多个ScrollView或者列表，开销相对较大。\n\n仔细分析这个场景和需求本身，父列表和当前子列表的浏览是用户行为的主体，而左右滑动切换子列表只是一个辅助行为，发生频率也应该偏低 那为实现辅助功能花费太多的实现代价 和运行时成本 并引入了更多不稳定因素 是不合理的 有无更合理的解决办法？另外 既然主体行为是在列表中浏览 思路转到是不是继仍然使用一个列表展示原父列表和当前子列表的内容 确保主体行为的流畅性 通过辅助手段解决辅助功能？\n\n基于这个思路和原有uiscrollview嵌套的方案作了个调整，把支持左右滑动的ScrollView移到最底层,内嵌一个列表来展示原有父列表和当前页面的内容(后面称这个列表为主列表)。当出现左右滑动的手势的时候，把原子列表的内容视图跟随手势进行位置偏移，创建辅助的列表展示其它分页内容，并加入底层的ScrollView跟随手势滑入滑出，形成页面滑动的效果。当左右滑动手势结束并确定切换到新的分页时，复原内容偏移并更新主列表的数据源，使其展示新分页的内容，更新数据源并刷新之后，移除辅助分页的列表。这个方案需要解决几个问题：\n1. 响应左右滑动的ScrollView由于在下层，滑动手势需要从上方列表透传下来；然后可响应左右滑动的区域需要局限在原子列表区域，通过手势响应的时候过滤位置可以解决。\n2. 当主列表切换到新分页数据源并移除辅助列表的时候，虽然相同位置展示相同的内容，但仍可能出现闪烁的情况。实践过程当中，特别是后面提到迁移到Android平台的时候，发现主要是图片加载更可能引起闪烁问题。通过图片缓存，或者场景复原等手段，能避免大多数图文列表的闪烁问题。更复杂的元素，如动画、视频，则需要针对性处理。\n3. 列表内部分内容视图做偏移的实现在不同的平台有不同的实现，iOS下面通过列表的Layout来实现，Android则直接对SubView做偏移。\n这个方案相比起来，左右滑动的实现集中在底层的ScrollView中，承载具体业务的列表只需要实现一些支持信息的提供，不过这种外层ScrollView套内层列表的方式还是存在结构不够清晰明了的问题。另外，如果需要有分页和无分页的随意切换，外部的ScrollView也比较不容易简单的卸载。\n\n在把这个方案迁移往Android平台的时候 发现RecyclerView自身提供了swipe手势的支持(ItemTouchHelper)，不需要通过ScrollView来支持左右滑动。成功改造成swipe手势来实现之后，又把这个思路搬回iOS，通过在列表中增加一个pan手势，也同样实现了左右滑动的能力.iOS下ScrollView对于分页有一些特殊支持，譬如通过滑动的距离来判断是否可以完成页面切换，手势停止之后的减速动画，这些都需要自行实现，算是本方案的难点，不过网上有相关的文档可以参考解决。\n\n至此，这个方案相对比较完美，结构和二次开发需要完成的接口也比较清晰。\n\n后续，为了解决不同分页切换存在的隐患，在考虑通过Layout来实现多个分页的方案，还在iOS下进行实验。\n\n参考：\n[Sticky Headers for UICollectionView using UICollectionViewFlowLayout](https://blog.radi.ws/post/32905838158/sticky-headers-for-uicollectionview-using)\n\n说明：\n1. 假设列表是纵向滚动 子列表切换是横向变换 (反之处理原理是一样的)\n2. iOS平台系统也提供了SwipeGesture，网评不如直接使用PanGesture好，未深究过。\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbluematthew%2Fpagingsublist","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbluematthew%2Fpagingsublist","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbluematthew%2Fpagingsublist/lists"}