一、效果图

左右丝滑滑动,并且有缩放动画。

  

二、分析和思路

1. 为什么选择用UICollectionView去做上面的效果?

  首先无限效果永远是表现出来的,而不是程序里面创建了无数个view,如何做到无限效果的视觉差这本身就是一个技术活。

  以我的知识水平,可以做无限效果的有三种方式:

  1). 三个view + 滑动手势。原理图如下:

  

  mid下面的承载view为工作区,负责添加滑动手势和根据手势滑动距离去修改left,mid,right三个view的位置和状态。当手势滑动结束的时候,需要在关闭隐式动画的基础上修改三个页面显示的内容并且重置三个view的位置和状态。

优点:逻辑简单,三个view的切换也不费事,比较的节省资源,因为是手势控制的,所以落点位置都比较好定位;

   缺点:只支持全景展示,不能做到一个显示框中显示三个item(否则就要用超过3个view的了),另外如果要实现手势的快滑和慢滑则又是另外一番努力了。

  2). UIScrollView:

  UIScrollView 就是在 1) 的基础上去掉自己添加的手势,用UIScrollView作为控制载体。而且UIScrollView提供了良好的滑动代理,有大量的api可以使用,只要用心是可以做出很多酷炫的效果。

  当然了,问题也很明显,代码控制复杂;做无限滚动的时候,到底创建多少个item比较合适?

  3). UICollectionView:

  用UICollectionView最大原因就是cell的重用规则,可以让你不去关心到底创建多少个item。而且UICollectionView是可以左右滑的。原理大约如下:

  

  滑动表现出来的无限只是因为在滑动停止后,重置了section。比如你想要sectionCount个section,section里面的rows就是广告的个数,初始状态下,一定是让屏幕中央显示的是indexPath为(sectionCount/2 ,0)的item,这样当向前滑动和向后滑动的时候就不会出问题,为了在快滑过程中更加丝滑柔顺,尽量的让sectionCount大点,,因为cell的重用机制让你可以不考虑这儿的内存消耗问题,我这儿给的是100。

  在滑动动画结束之后,需要关闭隐式动画条件下重置当前的滑动位置为indexPath=(sectionCount/2,currentRow),时机我选择在NSTimer的处理函数里面。

  到这儿基本上把无限循环的逻辑讲完了,再说一下item的缩放效果实现。

  从最开始给的效果图可以很容易得到一个结论:距离中心点位置越大,缩放比越大,屏幕中心点的缩放比最小为0,所以我们需要获取当前点到屏幕中心的distance;

  这个也是比较简单的,在 UICollectionViewFlowLayout 回调函数

- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect

  中可以轻松拿到,代码如下:  

    CGRect frame = CGRectZero;
frame.origin = self.collectionView.contentOffset;
frame.size = self.collectionView.size; for (UICollectionViewLayoutAttributes *attribute in array) {
//确立cell相对于屏幕中央的距离
CGFloat distance = CGRectGetMidX(frame) - attribute.center.x; //公式一
}

  为了计算出来下一步的缩放比,我们这儿需要约定一个参考宽度activeDistance,即,当前distance等于activeDistance时,缩放比最小到0,(离得越近,显示的越小,很符合人眼的观察习惯),为了更好的理解参考宽度,我们可以这样约定,距离中心距离为activeDistance,缩放比为scaleFactor,这样缩放比的计算公式为:

  CGFloat scale = 1 - (distance/activeDistance)*scaleFactor;    公式二

activeDistance和scaleFactor的值需要自己多次尝试,修改,我这儿给个建议值:


  activeDistance = 300.0;

  scaleFactor = 0.05;

最后把求出来的scale赋值给cell的attribute,完整代码如:

   - (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {

   NSArray *array = [super layoutAttributesForElementsInRect:rect];

       CGRect frame = CGRectZero;
frame.origin = self.collectionView.contentOffset;
frame.size = self.collectionView.size; for (UICollectionViewLayoutAttributes *attribute in array) {
//确立cell相对于屏幕中央的距离
CGFloat distance = CGRectGetMidX(frame) - attribute.center.x; //到中心位置的相对于x的比例,原则就是越近的越大,越远的越小。
CGFloat normalDistance = fabs(distance / self.activeDistance); CGFloat scale = - self.scaleFactor * normalDistance;
//属性赋值
attribute.transform3D = CATransform3DMakeScale(scale,scale, );
}
return array;
}

到这儿基本上就完了,除了最后一步,因为你把上述代码运行起来的时候就会发现,可以缩放,可以无限滚动,但是每次滚动之后item都不会自己跑到屏幕中央,这就需要修改回调

- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity

  这的逻辑很简单,在滑动结束后,屏幕中可能存在N个cell,从这N个cell中选择出离屏幕中央最近的cell,把选择出来的这个cell居中显示,就可以了。具体代码如下:

 // 确定最终滚到的位置
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity { CGRect targetRect = CGRectMake(proposedContentOffset.x, proposedContentOffset.y, self.collectionView.bounds.size.width, self.collectionView.bounds.size.height); NSArray *array = [super layoutAttributesForElementsInRect:targetRect]; CGFloat horizontalCenterX = proposedContentOffset.x + self.collectionView.bounds.size.width / .;
CGFloat offsetAdjustment = CGFLOAT_MAX;
for (UICollectionViewLayoutAttributes *attribute in array) {
//
CGFloat tempCenterX = attribute.center.x;
if (fabs(horizontalCenterX - tempCenterX) < fabs(offsetAdjustment)) {
offsetAdjustment = tempCenterX - horizontalCenterX;
}
} CGPoint resultPoint = CGPointMake(proposedContentOffset.x + offsetAdjustment, proposedContentOffset.y); return resultPoint;
}

三、结束

现在是真的完了,实现的思路和关键代码都列出来了,这儿再附一个demo地址:

https://github.com/goldBreak/Demos

     







iOS 利用UICollectionView做一个无限循环广告栏的更多相关文章

  1. (Android+IOS)我们正在做一个新闻App,做几乎一样的,倾听您的建议 (画画)

    (Android+IOS)我们正在做一个新闻App,做几乎一样的,倾听您的建议! 新闻采访是做,前端展示APP界面感觉还不是非常好,还须要改进改进,希望公布(Android和IOS版本号)前听听大家的 ...

  2. 利用jQuery实现图片无限循环轮播(不借助于轮播插件)

    原来我主要是用Bootstrap框架或者swiper插件实现轮播图的功能,而这次是用jQuery来实现图片无限循环轮播! 用到的技术有:html.css.JavaScript(少).jQuery(主要 ...

  3. iOS利用视频做起始页

    一个好的引导页会使得用户体验大大提升,利用视频来做,可以更简单的达到优雅的效果.使用MediaPlayer.framework框架下的AVPlayerLayer,它和Core Animation紧密地 ...

  4. SpringBoot2.x整合Email并利用AOP做一个项目异常通知功能

    因为不知aop能干嘛,因此用aop做个小功能,再结合最近学的springboot-Email做了个系统异常自动邮件通知的功能, 感觉满满的成就感. AOP不懂的可以看上一篇:https://www.c ...

  5. iOS 用collectionview 做的无限图片滚动 广告banner适用

    使用方法见demo,bug未知,若有什么问题欢迎留言:) http://files.cnblogs.com/files/n1ckyxu/NickyScrollImageView.zip demo使用s ...

  6. 利用jmeter做一个简单的性能测试并进行参数化设置

    1.新增一个线程组,并在下面添加基本原件,包括:监听器.http请求默认值和一个事务控制器 在http请求默认值中填写 ip 地址和端口号,协议类型默认为http 2.添加代理服务器,以便之后进行录制 ...

  7. 利用Django做一个简单的分页页面

    views代码: from django.shortcuts import render from django.conf import settings from booktest.models i ...

  8. 利用canvas做一个简单个gif停止和播放

    var ImagePlayer = function(options) { this.control = options.control; this.image = options.image; th ...

  9. canvas的进阶 - 学习利用canvas做一个炫酷的倒计时功能

    先给大家贴一张图片,因为我不会上传视频( ̄□ ̄||) ,请大家谅解了~  如果有知道怎么上传视频的大神还请指点指点 ^_^ ~ 然后看一下代码: html部分 :  <!DOCTYPE html ...

随机推荐

  1. 分析CPU使用率不断增加的原因

    工程中发现引起的问题: 结合别的朋友的意见,我的优化思路是: 1.排查是否内存泄漏 经过反复查询代码,未发现有内存泄漏(可以自己百度搜索C#内存泄漏的原因).可以通过任务管理器分析是否有内存泄漏,打开 ...

  2. Redis事务、持久化、发布订阅

    一.Redis事物 1. 概念 Redis 事务可以一次执行多个命令, 并且带有以下两个重要的保证: 事务是一个单独的隔离操作:事务中的所有命令都会序列化.按顺序地执行.事务在执行的过程中,不会被其他 ...

  3. $bzoj3872\ [Poi2014]\ Ant\ colony$ 二分+$dp$

    正解:二分+$dp$ 解题报告: 传送门$QwQ$ 一年过去了依然没有头绪,,,$gql$的$NOIp$必将惨败了$kk$. 考虑倒推,因为知道知道除数和答案,所以可以推出被除数的范围,然后一路推到叶 ...

  4. $Noip2012\ Luogu1081$ 开车旅行 倍增优化$ DP$

    Luogu Description Sol 1.发现对于每个城市,小A和小B的选择是固定的,可以预处理出来,分别记为ga[],gb[] 2.并且,只要知道了出发城市和出发天数,那么当前城市和小A,小B ...

  5. 语言篇:Java环境

    语言篇:Java环境 Java是什么? Java 是一项用于开发应用程序的技术语言,可以让 Web 变得更有意思和更实用.使用 Java 可以玩游戏.上载照片.联机聊天以及参与虚拟体验,并能够使用联机 ...

  6. Javascript用途,语法特点,难点,调试工具,引入方式,命名规范,变量声明及赋值,数据类型,运算符

    JavaScript用来干什么 数据的验证 将动态的内容写入到网页当中(ajax) 对事件做出相应 读写html当中的内容 检测浏览器 创建cookies 模拟动画 语法特点 基于对象和事件驱动的松散 ...

  7. selenium爬取驾考宝典题目

    要求 [x] Python3+ [x] Chrome驱动并已配置环境变量 [x] Selenium ## 研究页面 发现驾考宝典的科目四页面URL都是以 https://www.jiakaobaodi ...

  8. 【已解决】CentOS7使用yum安装Docker显示错误:cannot find a valid baseurl for repo: base/7/x86_64

    不得不说,Docker 要求 CentOS 系统的内核版本高于 3.10,这就让有些人开始头疼了,而要查看具体的版本可以用以下命令 uname -r 当然,CentOS 6.8版本也能安装Docker ...

  9. Django常用字段及参数、事务、数据库查询优化

    常用字段 注意: Django中没有设置对应char类型的字段,但可以支持自己定义. 自定义对应于数据库的char类型字段: from django.db.models import Field cl ...

  10. 解决httpclient设置代理ip之后请求无响应的问题

    httpclient这个工具类对于大家来说应该都不陌生吧,最近在使用过程中出现了碰到一个棘手的问题,当请求的接口地址由http变成https之后,程序执行到 httpClient.execute(ht ...