美文网首页iOS开发专题iOS 控件iOS开发实战
iOS手把手搭建·无限循环滚动视图

iOS手把手搭建·无限循环滚动视图

作者: Andy矢倉 | 来源:发表于2017-03-23 00:07 被阅读416次

    原文:iOS手把手搭建·无限循环滚动视图

    在大多数常规App开发当中,我们都会有产品运营栏的需求,也就是列表页或者产品顶部,又或者整个页面需要展示几个滚动的运营活动、产品、广告什么的,当然,也可能是一个自己实现的一个图片浏览器。
    在早些年,这类需求大多都是从First逐个滚动到Last,然后再自动滚到First,技术上无非都是通过UIScrollView + Timer的方案,iOS开发往往都喜欢专注于用(xuan)户(ji)体(zhuang)验(bi)的,所以后来出现了无限循环滚动的体验。

    得益于iOS6以后出现的UICollectionView控件,无论是滚动视图,还是做图片浏览,都降低了很多难度和代码量,但是它为了灵活性,官方没有做无限滚动Api,那么今天,我们就用UICollectionView来实现无限循环滚动视图。

    这里使用UICollectionView管理Cell方式来减少代码量和复用Cell的内存优化,通过关闭scrollToItem(at:at:animated:)滚动动画来让用户无法发觉是代码在控制滚动,让用户产生错觉变成无限循环。

    我们假设视图是在水平滚动,Cell是横屏全部宽度填充,然后设置paging属性为true以便滚动到边缘从而获得更好的体验。

    揣测

    原理:这么做依赖于有操作表的概念,这样我们就可以在收尾添加元素。好比如,你有一个包含三个项目的数组,想要他们无限循环的滚动,那就把首位元素拷贝插入到末尾,同时末尾元素拷贝一份插入到首部。演示如下:

    OK,我们直接上代码:

    private func setupDataForCollectionView() {
            let originalItems = ["One", "Two", "Three"]
            if let firstItem = originalItems.first, let lastItem = originalItems.last {
                var workItems = originalItems
                workItems.insert(lastItem, at: 0)
                workItems.append(firstItem)
                items = workItems
            }
    }
    

    那么我们得到的items的内部结构就是这样:

    ["Three", "One", "Two", "Three", "One"]
    

    结构上就和假想图一致。

    臆测

    这个过程依赖于在首尾的indexPath需要关闭动画来实现,通过方法scrollToItem(at:at:animated:)实现。
    该方法包含以下三个参数:

    • indexPathCollectionView滚动到的位置。
    • UICollectionViewScrollPosition来控制CollectionView应该滚动到什么位置。
    • animated这个布尔值控制是否展示动画。

    UICollectionViewScrollPosition控制滚动位置,假设CollectionView被设置了分页,如果是水平视图,我们希望它滚到左边那就为UICollectionViewScrollPosition.left,如果是垂直视图,希望它滚到顶部那就是UICollectionViewScrollPosition.top

    下面我们来看看是演示情况:


    如果是往前滚的话就正好相反:


    实施

    我们实现的关键技术点就是检测用户的滚动意图,这样才能触发scrollToItem(at:at:animated:)方法来实现我们的目的。
    为了能做到这一点,我们需要实现UICollectionView的父类的UIScrollView的代理方法scrollViewDidEndDecelerating来检测滚动停止信号。
    再通过检测contentOffset属性来判断具体位置。

    override func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        let contentOffsetX = scrollView.contentOffset.x
        let contentOffsetWhenFullyScrolledRight = (collectionView?.frame.width)! * CGFloat(items.count - 1)
        if contentOffsetX == contentOffsetWhenFullyScrolledRight {
            let indexPath = IndexPath.init(item: 1, section: 0)
            collectionView?.scrollToItem(at: indexPath, at: .left, animated: false)
        } else if contentOffsetX == 0 {
            let indexPath = IndexPath.init(item: (items.count - 2), section: 0)
            collectionView?.scrollToItem(at: indexPath, at: .left, animated: false)
        }
    }
    

    OK,我们来看下视图结构和对应的索引结构:


    视图结构能清晰的解答:

    • 如果在我们滚到最右边,所看到的元素为我们拷贝的第一个元素,那么就应该调用scrollToItem(at:at:animated:)方法来滚动到实际上的第一个元素位置,也就是索引[0, 1]的位置。
    • 如果我们滚动到最左边,所看到的元素为我们拷贝的最后一个元素,那么就应该调用scrollToItem(at:at:animated:)方法来滚动到实际上的最后一个元素位置,也就是索引[0, 3]的位置,也就是处理位置里的items.count - 2位置。

    总结

    创建一个无限循环的滚动视图其实So Easy,也就五个步骤:

    • 根据实际数据,填充收尾的假数据。
    • 检查滚动视图滚动停止时的偏移位置。
    • 如果滚动到最末尾,则移动到填充过的数据项中的第二项。
    • 如果滚动到最首位,则移动到填充过的数据项中的最后一个数据项。
    • 保证方法scrollToItem:里的animated动画参数为关闭。

    相关文章

      网友评论

        本文标题:iOS手把手搭建·无限循环滚动视图

        本文链接:https://www.haomeiwen.com/subject/bkrnnttx.html