这是我新近完成的一个自定义横滑组件,本文回忆一下当时完成过程遇到的问题和细节,最终有源码地址

文中一切图片保管在Github上

所谓横滑组件其实就如图所示的作用:

iOS横滑组件实现

列一下UI上的要求:

  • 每次滑动一页,有pageEnable的作用
  • 每次显现在屏幕中的item其实是三个而不是一个
  • 每个item的间距、视图与屏幕边缘的边距严格按照UI上姿态

UICollectionView+pageEnable

运用UICollectionView并敞开pageEnable是最容易想到的计划,咱们来试一下能否满足需求

要害的几个参数如下所示

container.width = 375
collectionView.isPagingEnable = true
collectionView.width = 375
leftPadding = rightPadding = 16
cell.width = container.width - leftPadding - rightPadding
collectionView.contentInset = UIEdgeInset(0,16,0,0)

作用如下所示:

iOS横滑组件实现

显然,没有到达预期:

  • 问题1,每次滑动中止后,cell的位置不对
    • 经过打印contentOffset得知,UIScrollView敞开pagingEnable后的主动翻页,每次修改contentOffset的值等于UIScrollView.width
    • 并且咱们无法自定义每次翻页移动的间隔
  • 问题2,由于设置了collectionView.contentInset.left,所以榜首cell能够移动到屏幕最左边而不能主动复原到初始位置

不甘心,持续调整

我画了一张图来表示要完成的作用:

iOS横滑组件实现

  • 依据上图的作用,咱们期望的作用是每次移动cell时移动的间隔(两条红竖线之间的间隔)是一个cell的宽度+cell之间的间隔–cell.width+interval
  • 既然pageEnable特性每次移动的间隔一定是scrollView.width,所以咱们能够让scrollView.width = cell.width+interval
  • 这或许能处理上面显现反常问题

咱们更新一下装备参数,如下:

leftPadding = rightPadding = 16
container.width = 375
collectionView.isPagingEnable = true
cell.width = container.width - leftPadding - rightPadding
interval = 8
collectionView.width = cell.width + interval
collectionView.contentInset = UIEdgeInset(0,0,0,interval) // 这一句或许会引起你的困惑,但经过测试有必要设置成这样,否则作用有问题,本文不做详细解释,跟scrollView本身关于contentSize和contentOffset的调整有关

来看一下作用:

iOS横滑组件实现

哇,如同不错!但还是有问题:

  • 咱们期望一起显现三个cell,但该作用却只能显现1个cell
  • 这是因为collectionView的宽度刚好能显现下一个cell和一个interval,没有更多空间来显现其他cell了

这就很为难了,为了运用pageEnable的特性,咱们不得不修改collectionView的宽度小一些,但这却导致无法足够的cell个数

所以,结论是:❌

UICollectionView + UIScrollView

在调研其他技术计划时,受一Paging a overflowing collection view启发,能够运用一个UICollectionView和一个UIScrollView一起完成相似作用

中心思维如下:

  • 单独用一个UIScrollView,运用pageEnable特性来完成符合要求的横滑、拖拽翻页作用
  • 单独用一个UICollectionView来运用它的cell显现、复用机制
  • UIScrollView是不显现的,只用它的拖拽手势能力。当拖拽UIScrollView时,将contentOffset的移动应用到UICollectionView中

具体完成过程中有些细节需求留意,比方:

  1. collectionView的contentInset需求设置
  2. 将scrollView的移动应用到collectionView中时如何计算准确
  3. 需求关闭collectionView的panGesture

再放一下作用

iOS横滑组件实现

结论是:✅

源码地址:SlideView.swift

优缺点

长处很明显:

  • 既复用了UIScrollView的pageEnable手势和动画作用,也复用了UICollectionView的cell复用机制
  • 由于复用了UICollectionView,所以比较经过UIScrollView自定义完成,在一些用户交互体验上或许更好,比方在快速横滑时,自定义的完成或许就没办法快速的准备好每一个cell并无缝从上一页切换过来,或许会有点卡顿
  • 一切完成细节都是经过系统官方的public API,不存在任何trick行为,稳定性好

缺点:

在用户体验上没发现缺点。只是在封装为独立组件时需求留意更多细节,比方:

  • 该组件将CollectionView封装了起来,所以有必要给外部运用者暴露dataSource和delegate等必要的回谐和数据源办法

运用UIScrollView完全自定义完成

我还看过另一种计划:

  • 自己创立cell视图,添加到UIScrollView上
  • 完全由自己来操控cell的复用和显现逻辑
  • 滑动手势和作用方面,运用UIScrollViewDelegate办法来操控抬起手指后移动到到下一个或上一个cell的作用(该作用我曾经也完成过,能够参考设计与Swipe-Delete不冲突的UIPageViewController)

这个思路看上去应该是可行的,我也看过相似的源码完成,是Github上的一个代码

但该源码的显现逻辑写的不好:

  • 每次切换cell时,会一起经过delegate要求更新一切的cell数据(显现在屏幕中的cell和在缓存池中未用到的cell)