UICollectionView 横向停止的两种效果。

类似于 Airbnb 这款App的首页酒店效果,从最开始的减速停止效果,到现在的分页效果。

本文主要说一下Demo的关键类及代码的使用, 还有算法的大概思路。看下面~

使用方式

关键类:

注意: Demo类中使用的布局方式是第三方约束 Masonry ,请使用Pods自行导入到项目中。

部分代码说明:

#import <UIKit/UIKit.h>typedef NS_ENUM(NSUInteger, WBScrollType) {WBScrollTypeFree ,      //自由减速效果WBScrollTypePage        //分页的效果
};@interface UIContainerCollectionView : UIView- (void)setupWithDataSource:(NSArray<id> *)dataSource pointValue:(NSValue *)pointValue;@property (nonatomic, copy) void (^pointChangeBlock)(NSValue *pointValue);@property (nonatomic, assign) WBScrollType scrollType;      //默认是分页的@end

UIContainerCollectionView 是 UICollectionView 的容器类,直接在需要使用的地方创建 UIContainerCollectionView 即可。

- setupWithDataSource: pointValue ;  //设置数据源 和 当前的滑动到的初始位置。 (pointValue 为了解决重用的问题)

void (^pointChangeBlock)(NSValue *pointValue);//每次滑动CollectionView, 都会把停止的位置回传出来,在VC记录,也是为了解决重用问题

scrollType             //设置滑动停止类型。 默认是分页效果。 设置为WBScrollTypeFree 为减速效果。

下面的代码是创建在 UITableViewCell 里面的:

- (void)setupUI {WEAK_SELF();self.containerView = [UIContainerCollectionView new];//默认是 WBScrollTypePage 分页self.containerView.scrollType = WBScrollTypeFree;[self.containerView setPointChangeBlock:^(NSValue *pointValue) {weakSelf.pointChangeBlock(pointValue);}];[self.contentView addSubview:self.containerView];[self.containerView mas_makeConstraints:^(MASConstraintMaker *make) {make.top.leading.trailing.bottom.equalTo(@0);}];
}- (void)setupWithDataSource:(NSArray<id> *)dataSource pointValue:(NSValue *)pointValue {[self.containerView setupWithDataSource:dataSource pointValue:pointValue];
}

使用方式直接创建就行了,数据源再赋值一下就ok了~~

实现思路 :

首先都是要实现 UIScrollViewDelegate ,及下面这段代码 (UIScrollView 的减速Delegate)

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {CGPoint estimateContentOffset = CGPointMake(targetContentOffset -> x, targetContentOffset -> y);CGPoint currentPoint = [self itemCenterOffsetWithOriginalTargetContentOffset:estimateContentOffset];self.pointChangeBlock([NSValue valueWithCGPoint:currentPoint]);*targetContentOffset = currentPoint;
}

这个方法能在你拖动,甩动ScrollView ,手指离开时会调用此方法,这个会提前计算出ScrollView 最终大概会停止的位置  targetContentOffset 。

然后我们要根据这个位置,自己计算出 需要停止的合适的具体位置 ,再传给 targetContentOffset。

在初始化 UICollectionView 的时候, 就要设置好 滑动的减速速度 decelerationRate ,这个值等于 1.0f 的时候,速度最慢,就会是减速运动。 等于0.1f 的时候,就会是分页效果(分页效果这只是其中的一个条件,为0.1f)。

具体看Demo中的这个方法 :

- (CGPoint)itemCenterOffsetWithOriginalTargetContentOffset:(CGPoint)orifinalTargetContentOffset {if (self.scrollType == WBScrollTypeFree) {  //自由惯性的CGFloat pageWidth = self.contentSizeWidth / (CGFloat)self.imageNameds.count;NSUInteger cellWidth = (self.collectionView.width - CONTENTOFFSET_X * 2 - 10 ) / 2.0;NSInteger row = 0;CGPoint point ;if (orifinalTargetContentOffset.x <= pageWidth / 2.0) {row = 0;point = CGPointMake(0 - CONTENTOFFSET_X, 0);self.collectionView.contentInset = UIEdgeInsetsMake(0, CONTENTOFFSET_X, 0, 0);return point;}if (orifinalTargetContentOffset.x > self.contentSizeWidth - cellWidth * 2.5 + 20) {row = self.imageNameds.count - 2;point = CGPointMake(row * (cellWidth + CONTENTOFFSET_X / 2.0) - CONTENTOFFSET_X, 0);self.collectionView.contentInset = UIEdgeInsetsMake(0, 0, 0, CONTENTOFFSET_X);return point;}NSUInteger index = orifinalTargetContentOffset.x / pageWidth;row = index + (orifinalTargetContentOffset.x - pageWidth * index > pageWidth / 2.0 ? 1 : 0);point = CGPointMake(row * (cellWidth + CONTENTOFFSET_X / 2.0) - CONTENTOFFSET_X, 0);return point;} else {CGFloat pageWidth = self.contentSizeWidth / (CGFloat)self.imageNameds.count;NSUInteger cellWidth = (self.collectionView.width - CONTENTOFFSET_X * 2 - 10 ) / 2.0;NSInteger row = 0;CGPoint point ;CGFloat scrollBeforeContentOffsetX = self.row * ((cellWidth + CONTENTOFFSET_X / 2.0) - CONTENTOFFSET_X);    //滑动之前的 X 位置NSUInteger scrollDirection = orifinalTargetContentOffset.x - scrollBeforeContentOffsetX >= 0.0 ? 1 : 0;    //1向右, 0向左if (orifinalTargetContentOffset.x == -CONTENTOFFSET_X) {scrollDirection = 0;}if (fabs(orifinalTargetContentOffset.x - scrollBeforeContentOffsetX) > pageWidth * 1.5) {// + 2row = (scrollDirection == 0) ? (self.row - 2) : (self.row + 2);} else {// + 1row = scrollDirection == 0 ? (self.row - 1) : (self.row + 1);}row = row < 0 ? 0 : row;row = row > self.imageNameds.count - 2 ? (self.imageNameds.count - 2) : row;self.row = row;if (row == 0) {self.collectionView.contentInset = UIEdgeInsetsMake(0, CONTENTOFFSET_X, 0, 0);}if (row == self.imageNameds.count - 2) {self.collectionView.contentInset = UIEdgeInsetsMake(0, 0, 0, CONTENTOFFSET_X);}point = CGPointMake(row * (cellWidth + CONTENTOFFSET_X / 2.0) - CONTENTOFFSET_X, 0);return point;}return CGPointMake(0, 0);
}
 

这个方法首先分了两种情况,

这里只说下自由减速的思路,把系统告诉我们大概停下的位置,除以 pageWidth, 能获得一个至少要停止的整数值index (假设我们这边index == 5),那我们到底是停在 5 还是 6 呢? 这个还要取决于系统告诉我们的位置是否大于 5.5 (暂时这是比较合理的需求吧~当然你也可以是5.3 或者 5.8) , 所有就有下面的:

row = index + (orifinalTargetContentOffset.x - pageWidth * index > pageWidth / 2.0 ? 1 : 0);

最终再计算出point 返回给系统,就能停止到指定的位置了。

分页效果的思路:

和减速不同的是,我们要确定分页一次最多能滑动几页,这个Demo是一般滑动都是一页,如果最用力的滑,是两页。 所以计算范围 就不能像减速那样自由。

如果滑动停止的位置是在一页到两页的宽度之间,那么 row就在原来的基础上 +1 ,如果超过两页的距离, 就加 +2 。 并且这里还要记录下 滑动的方向,向左的话就 -1 或者 -2。 方向的判断,可以用 滑动之前的位置(a) 和 停止位置(b) 做比较,  如果 b - a > 0 说明向右滑动,反之向左。

最终返回合适的point 。

效果样式图

分页效果:

减速效果:

Demo下载地址:

http://download.csdn.net/detail/yutianlong9306/9583643

补充一个 Item的 UI 效果。

效果图:

说明:

这个Demo中,是实现了一个Item的计算方式,并且做了一些极端情况的优化,更友好的滚动交互。

Demo下载地址:

http://download.csdn.net/detail/yutianlong9306/9623331

UICollectionView 横向滑动停止的两种效果。相关推荐

  1. android 横向头像栏,GitHub - liushiqi0112/android-headimage-cliper: 头像上传图片裁剪,实现仿QQ、微信两种效果...

    Android头像上传之图片裁剪,实现仿QQ.微信两种效果 功能描述: 头像上传在APP中是很常见的功能,但是关于头像上传前的图片裁剪方式,如果使用系统提供的裁剪方式达不到预期效果,所以在这里提供一个 ...

  2. 《Photoshop修色圣典(第5版)》—第1章1.10节何时两种效果最佳

    本节书摘来自异步社区<Photoshop修色圣典(第5版)>一书中的第1章1.10节何时两种效果最佳,作者[美]Dan Margulis,更多章节内容可以访问云栖社区"异步社区& ...

  3. android吸附菜单,Android RecycleView实现滑动停止后自动吸附效果

    Android RecycleView实现滑动停止后自动吸附效果 发布时间:2020-11-02 16:01:42 来源:亿速云 阅读:124 作者:Leah 这篇文章将为大家详细讲解有关Androi ...

  4. 视频剪辑技巧,同时剪辑两种效果操作

    遇到想要批量剪辑视频时,如何给视频同时添加上两种效果剪辑呢?下面就以给多个视频添加滚动字幕水印,并让视频有渐入效果播放为例,一起来操作批量剪辑多个视频的步骤,希望以后可以用到. 准备工具: 下载一个视 ...

  5. Latex生成两种效果的表格

    常见的使用Latxe生成两种效果的表格,其实现区别在于生成横线的方式不同: 第一种是直接使用\hline生成横线 第二种是使用\toprule顶部线,\midrule中间线,\bottomrule底部 ...

  6. php滑动拼图验证,JS实现PC手机端和嵌入式滑动拼图验证码三种效果

    PC和手机端网站滑动拼图验证码效果源码,同时包涵了弹出式Demo,使用ajax形式提交二次验证码所需的验证结果值,嵌入式Demo,使用表单形式提交二次验证所需的验证结果值,移动端手动实现弹出式Demo ...

  7. android中view手势滑动冲突的两种解决方法

    android中view手势滑动冲突的解决方法,主要解决方法有两种,外部和内部拦截.有需要的可以参考下. Android手势事件的冲突跟点击事件的分发过程息息相关,由三个重要的方法来共同完成,分别是: ...

  8. 手机HTML拼图验证,JS实现PC手机端和嵌入式滑动拼图验证码三种效果_气质_前端开发者...

    PC和手机端网站滑动拼图验证码效果源码,同时包涵了弹出式Demo,使用ajax形式提交二次验证码所需的验证结果值,嵌入式Demo,使用表单形式提交二次验证所需的验证结果值,移动端手动实现弹出式Demo ...

  9. 搭建积分商城系统需要突出的两种效果

    积分商城系统是积分体系运营中帮助商家进行用户转化和拉新的场所,也是商家不得不重视的一个版块,积分商城主要配合积分进行使用,当用户获得了大量的积分后,就可以直接在积分商城进行兑换,当兑换到商品后,用户对 ...

最新文章

  1. 藏在1.85亿人体内的隐形致癌病毒,有人确诊即是晚期
  2. Vertica 高可用性测试
  3. 关于Update语句在不同数据库中的差别
  4. element-ui踩坑
  5. 深入浅出——网络模型中Inception的作用与结构全解析
  6. thinkphp 删除该表的最后一行
  7. 90后实习生,是如何成长为阿里云分布式NoSQL领域专家
  8. c# 保存和打开文件的方法
  9. Android开发问题集锦-Button初始为disable状态时自定义的selector不生效问题
  10. 58天 -算法 openJudge百炼 2787-凑24 - 递归
  11. 小莫微信影视机器人-自定义对接影视教程
  12. 神策分析1.7重磅上线 三大功能直指精益数据分析
  13. Invest模型问题答疑--产水模块、土壤保持模块、供需平衡分析、生态系统服务、生物多样性生境质量
  14. html好看鼠标光标特效
  15. RNNoise降噪训练
  16. Vmware Fusion
  17. onselect与onselectstart 禁止选中
  18. 高保密单位数据安全怎么办?用这款知识管理系统
  19. 根据域名查询外网ip
  20. LayaBox微信小游戏截图功能 利用微信API实现完美截图

热门文章

  1. 18-EMM Procedure 6. Handover without TAU - Part 3. S1 Handover
  2. 显示杂谈(1)-Gamma到底是个什么鬼
  3. VS中C++解决方案中多个项目文件的引用
  4. Spring源码系列(十二)Spring创建Bean的过程(二)
  5. C语言arduino密码锁实验报告,简易密码锁的制作-Arduino中文社区 - Powered by Discuz!...
  6. 关于Python将plt文件坐标转换为TXT文本
  7. element-ui手风琴自定义html,element-ui中el-table expand 手风琴效果,展开里面的内容或者ta...
  8. android控制wifi,Android控制wifi开关
  9. python标准数据类型叮叮叮
  10. Linux下shell脚本的编写