[iOS] UICollectionView实现图片水平滚动
最新更新: 简单封装了一下代码,参考新文章:UICollectionView实现图片水平滚动
先简单看一下效果:
新博客:http://wossoneri.github.io
准备数据
首先先加入一些资源文件:
先建立一个xcassets
文件,放入图片:
再建立一个plist文件,写入与图片对应的内容:
在ViewController中读取plist
到词典中:
@property (nonatomic, strong) NSArray *itemTitles;
NSString *path = [[NSBundle mainBundle] pathForResource:@"titles" ofType:@"plist"];
NSDictionary *rootDictionary = [[NSDictionary alloc] initWithContentsOfFile:path];
self.itemTitles = [rootDictionary objectForKey:@"heros"];
可以打log
输出,可以看到plist
的内容已经读取出来,后面就可以用_itemTitle
作为数据源了。
添加UICollectionView初步显示图片
每个CollectionView
都有一个对应的布局layout
,对于默认的的UICollectionViewFlowLayout
,效果是类似Android的GridView
的布局。如果要自定义CollectionView
的样式,就要对这个layout
进行修改。
建立自己的HorizontalFlowLayout
,继承自UICollectionViewFlowLayout
,然后在初始化方法里将滚动方向设置为水平:
- (instancetype) init {
if (self = [super init]) {
self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
}
return self;
}
接下来定制我们的cell
的显示样式,建立DotaCell
,继承自UICollectionViewCell
。由于我们要实现的是图片和文字的上下布局,所以增加两个属性:
@interface DotaCell : UICollectionViewCell
@property (nonatomic, strong) UIImageView *image;
@property (nonatomic, strong) UILabel *name;
@end
然后设置图片与文字上下对齐布局,这里我使用pod
导入Masonry
库来写自动布局:
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self initialize];
}
return self;
}
- (void)initialize {
self.layer.doubleSided = NO;
self.image = [[UIImageView alloc] init];
self.image.backgroundColor = [UIColor clearColor];
self.image.contentMode = UIViewContentModeCenter;
self.image.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.name = [[UILabel alloc] init];
self.name.font = [UIFont fontWithName:@"Helvetica Neue" size:20];
self.name.textAlignment = NSTextAlignmentCenter;
[self.contentView addSubview:self.image];
[self.contentView addSubview:self.name];
[_image mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.equalTo(self.contentView);
make.top.equalTo(self.contentView).offset(30);
make.bottom.equalTo(_name.mas_top).offset(-10);
}];
[_name mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.equalTo(self.contentView);
make.top.equalTo(_image.mas_bottom).offset(10);
make.bottom.equalTo(self.contentView).offset(-20);
}];
}
写好layout
和cell
后就可以用这两个类来初始化我们的collectionView
了:
//add in view did load
self.layout = [[HorizontalFlowLayout alloc] init];
CGRect rct = self.view.bounds;
rct.size.height = 150;
rct.origin.y = [[UIScreen mainScreen] bounds].size.height / 2.0 - rct.size.height;
self.collectionView = [[UICollectionView alloc] initWithFrame:rct collectionViewLayout:_layout];
self.collectionView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.collectionView.showsHorizontalScrollIndicator = NO;
self.collectionView.decelerationRate = UIScrollViewDecelerationRateNormal;
[self.collectionView registerClass:[DotaCell class] forCellWithReuseIdentifier:NSStringFromClass([DotaCell class])];
[self.collectionView setBackgroundColor:[UIColor clearColor]];
[self.collectionView setDelegate:self];
[self.collectionView setDataSource:self];
[self.view addSubview:_collectionView];
添加UICollectionViewDataSource
的代理方法,使其显示数据。
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return [self.itemTitles count];
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
DotaCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:NSStringFromClass([DotaCell class]) forIndexPath:indexPath];
cell.image.image = [UIImage imageNamed:[self.itemTitles objectAtIndex:indexPath.row]];
cell.name.text = [self.itemTitles objectAtIndex:indexPath.row];
return cell;
}
这样程序就有了我们想要的初步效果:
图片水平排放
但...效果的确很差!
下面要做的就是逐步完善效果,首先我们要让两排图像变成一排去展示。那要怎么去做?首先,我们在初始化collectionView
的地方设置了高度为150,所以图片就挤在这个150的高度里尽可能的压缩显示。由于collectionView
的尺寸已经设定,那么就剩cell
的尺寸可以控制了。实现CollectionViewFlowLayoutDelegate
的代理方法sizeForItemAtIndexPath
:
- (CGSize)collectionView:(nonnull UICollectionView *)collectionView layout:(nonnull UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(nonnull NSIndexPath *)indexPath {
return CGSizeMake(64, collectionView.bounds.size.height);
}
这里宽度64是图片的尺寸,高度设置填满
collectionView
的高度是为了防止上图中两行图片挤压的情况,所以直接让一个cell
的高度占满整个容器。
这时候的效果好了很多,已经有点样子了:
顶端图片滑到中间
但这离我们最终的效果还差很远,接下来我需要实现让第一张图片和最后一张图片都能滑到屏幕中点的位置,这应该是很常见的效果,实现起来也很简单。首先我们的一排cell
都默认为顶端与collectionView
的两端对齐的,collectionView
的左右两端与viewController.view
也是对齐的,所以显示的效果是,两端的图片都与屏幕对齐。知道这个关系就好办了,直接设置collectionView
与其父view
的内间距即可。
依旧是实现flowLayout
的代理方法:
//Asks the delegate for the margins to apply to content in the specified section.安排初始位置
//使前后项都能居中显示
- (UIEdgeInsets)collectionView:(nonnull UICollectionView *)collectionView layout:(nonnull UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
NSInteger itemCount = [self collectionView:collectionView numberOfItemsInSection:section];
NSIndexPath *firstIndexPath = [NSIndexPath indexPathForItem:0 inSection:section];
CGSize firstSize = [self collectionView:collectionView layout:collectionViewLayout sizeForItemAtIndexPath:firstIndexPath];
NSIndexPath *lastIndexPath = [NSIndexPath indexPathForItem:itemCount - 1 inSection:section];
CGSize lastSize = [self collectionView:collectionView layout:collectionViewLayout sizeForItemAtIndexPath:lastIndexPath];
return UIEdgeInsetsMake(0, (collectionView.bounds.size.width - firstSize.width) / 2,
0, (collectionView.bounds.size.width - lastSize.width) / 2);
}
效果如图:
居中图片放大显示
接下来添加一个我们需要的特效,就是中间的图片放大显示,其余的缩小并且增加一层半透明效果。
在FlowLayout
中有一个名为layoutAttributesForElementsInRect
的方法,功能如其名,就是设置范围内元素的layout
属性。对于这个效果,首先需要设置放大的比例,其次要根据图片大小和间距来设定一个合适的触发放大的区域宽度,当图滑入这个区域就进行缩放。
static CGFloat const ActiveDistance = 80;
static CGFloat const ScaleFactor = 0.2;
//这里设置放大范围
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
NSArray *array = [super layoutAttributesForElementsInRect:rect];
CGRect visibleRect = (CGRect){self.collectionView.contentOffset, self.collectionView.bounds.size};
for (UICollectionViewLayoutAttributes *attributes in array) {
//如果cell在屏幕上则进行缩放
if (CGRectIntersectsRect(attributes.frame, rect)) {
attributes.alpha = 0.5;
CGFloat distance = CGRectGetMidX(visibleRect) - attributes.center.x;//距离中点的距离
CGFloat normalizedDistance = distance / ActiveDistance;
if (ABS(distance) < ActiveDistance) {
CGFloat zoom = 1 + ScaleFactor * (1 - ABS(normalizedDistance)); //放大渐变
attributes.transform3D = CATransform3DMakeScale(zoom, zoom, 1.0);
attributes.zIndex = 1;
attributes.alpha = 1.0;
}
}
}
return array;
}
效果如下:
滑动校正
这时候几乎完成了,但还差点东西,就是让其在滚动停止的时候,离屏幕中间最近的cell
自动矫正位置到中间。还是在FlowLayout
添加该方法,具体说明我都写到注释里了:
//scroll 停止对中间位置进行偏移量校正
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity {
CGFloat offsetAdjustment = MAXFLOAT;
//// |-------[-------]-------|
//// |滑动偏移|可视区域 |剩余区域|
//是整个collectionView在滑动偏移后的当前可见区域的中点
CGFloat centerX = proposedContentOffset.x + (CGRectGetWidth(self.collectionView.bounds) / 2.0);
// CGFloat centerX = self.collectionView.center.x; //这个中点始终是屏幕中点
//所以这里对collectionView的具体尺寸不太理解,输出的是屏幕大小,但实际上宽度肯定超出屏幕的
CGRect targetRect = CGRectMake(proposedContentOffset.x, 0.0, self.collectionView.bounds.size.width, self.collectionView.bounds.size.height);
NSArray *array = [super layoutAttributesForElementsInRect:targetRect];
for (UICollectionViewLayoutAttributes *layoutAttr in array) {
CGFloat itemCenterX = layoutAttr.center.x;
if (ABS(itemCenterX - centerX) < ABS(offsetAdjustment)) { // 找出最小的offset 也就是最中间的item 偏移量
offsetAdjustment = itemCenterX - centerX;
}
}
return CGPointMake(proposedContentOffset.x + offsetAdjustment, proposedContentOffset.y);
}
增加图片点击效果
最后 添加一个点击cell 将其滚动到中间
在viewcontroller
添加CollectionViewDelegate
的代理方法
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
[self.collectionView selectItemAtIndexPath:indexPath animated:YES scrollPosition:UICollectionViewScrollPositionNone];
//滚动到中间
[self.collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];
}
封装成控件
当我们把效果实现之后,就可以考虑将代码优化一下,合到一个类里,减少书写常量,增加接口,封装成一个控件去使用。比如可以设定文字的显示与隐藏接口,再比如增加适应各种尺寸的图片等等。这个代码就不放了,毕竟不难,有问题给我留言好了。
[iOS] UICollectionView实现图片水平滚动的更多相关文章
- 【demo练习三】:图片水平滚动、点击按钮变更图片动画
要求:四张图片水平滚动,每隔5秒进行一次循环,点击按钮随机变更图片. XAML前台代码: <Window x:Class="图片滚动.MainWindow" xmlns=&q ...
- [iOS] WSHorizontalPickerView 图片水平滚动封装
之前这篇文章传送门本来是记录自己练手的demo的,后来很多人来问我要代码.今天就抽时间封装了一下,没有考虑太多情况,等我有空再去仔细考虑吧. 代码在:Github 用法很简单,创建对象,设置数据源,记 ...
- JS特效——图片水平滚动
具体源码如下: <!doctype html> <html lang="en"> <head> <meta http-equiv=&quo ...
- Android_ViewPager_实现多个图片水平滚动
1.示意图 2.实现分析 (1).xml配置 <!-- 配置container和pager的clipChildren=false, 并且指定margi ...
- Android ViewPager实现多个图片水平滚动
1.示意图 2.实现分析 (1).xml配置 <!-- 配置container和pager的clipChildren=false, 并且指定margi ...
- iOS开发之多图片无缝滚动组件封装与使用
经常有园友会问"博主,有没有图片无限滚动的Demo呀?", 正儿八经的图片滚动的Demo我这儿还真没有,今天呢就封装一个可以在项目中直接使用的图片轮播.没看过其他iOS图片无限轮播 ...
- IOS UIScrollView + UIButton 实现segemet页面和顶部标签页水平滚动效果
很长一段时间没有写博客了,最近在学习iOS开发,看了不少的代码,自己用UIScrollView和UIButton实现了水平滚动的效果,有点类似于今日头条的主界面框架,效果如下: 代码如下: MyScr ...
- ASP.NET中使用JavaScript实现图片自动水平滚动效果
参照网上的资料,在ASP.NET中使用JavaScript实现图片自动水平滚动效果. 1.页面前台代码: <%@ Page Language="C#" AutoEventWi ...
- 使用Recyclerview实现图片水平自动循环滚动
简介: 本篇博客主要介绍的是如何使用RecyclerView实现图片水平方向自动循环(跑马灯效果) 效果图: 思路: 1.准备m张图片 1.使用Recyclerview实现,返回无数个(实际Inter ...
随机推荐
- 继承extends、super、this、方法重写overiding、final、代码块_DAY08
1:Math类的随机数(掌握) 类名调用静态方法. 包:java.lang 类:Math 方法:public static double random(): Java.lang包下的类是不用导包就可 ...
- (转) lsof 一切皆文件
原文:https://linuxtools-rst.readthedocs.io/zh_CN/latest/tool/lsof.html lsof(list open files)是一个查看当前系统文 ...
- Git学习系列之Debian或Ubuntu上安装Git详细步骤(图文详解)
前言 最早Git是在Linux上开发的,很长一段时间内,Git也只能在Linux和Unix系统上跑.不过,慢慢地有人把它移植到了Windows上.现在,Git可以在Linux.Unix.Mac和Win ...
- es-02-elasticsearch安装及遇到的问题
最近因为工作需要, 又使用到了es, 版本已经从当年的2.4 更新到了6.3 基本上解压即用, elasticsearch 5.x 版本, 在 centos6下, 很多性能不能够发挥, 建议 cent ...
- postgresql逻辑结构--用户及权限管理(七)
一.用户和角色 二.创建用户和角色 三.权限管理 四.
- redis开机自动服务脚本
1.设置redis.conf中daemonize为yes,确保守护进程开启. 2.编写开机自启动脚本 基本原理为: 系统开机启动时会去加载/etc/init.d/下面的脚本,通常而言每个脚本文件会自定 ...
- fastjson 处理 double 的精度问题
项目中使用 fastjson 来处理 json 格式,当前使用的版本为1.1.37.在和其它系统交互时,将一个json串传给了对方,原值为5.0,json 处理后格式为:{"dou" ...
- MYSQLI_USE_RESULT or MYSQLI_STORE_RESULT
之前都是使用同事封装好的mysql类,今天做性能测试时自己手动编写了查询mysql的操作.偶然发现mysqli::query(或者mysqli_query)有一个参数$resultmode取值为MYS ...
- 谈谈Windows Wow64
欢迎转载,转载请注明出处:http://www.cnblogs.com/lanrenxinxin/p/4977488.html 本文是<深入理解Windows操作系统 (第六版) >关于6 ...
- Redis Cluster高可用集群在线迁移操作记录
之前介绍了redis cluster的结构及高可用集群部署过程,今天这里简单说下redis集群的迁移.由于之前的redis cluster集群环境部署的服务器性能有限,需要迁移到高配置的服务器上.考虑 ...