最新更新: 简单封装了一下代码,参考新文章: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);
}];
}

写好layoutcell后就可以用这两个类来初始化我们的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实现图片水平滚动的更多相关文章

  1. 【demo练习三】:图片水平滚动、点击按钮变更图片动画

    要求:四张图片水平滚动,每隔5秒进行一次循环,点击按钮随机变更图片. XAML前台代码: <Window x:Class="图片滚动.MainWindow" xmlns=&q ...

  2. [iOS] WSHorizontalPickerView 图片水平滚动封装

    之前这篇文章传送门本来是记录自己练手的demo的,后来很多人来问我要代码.今天就抽时间封装了一下,没有考虑太多情况,等我有空再去仔细考虑吧. 代码在:Github 用法很简单,创建对象,设置数据源,记 ...

  3. JS特效——图片水平滚动

    具体源码如下: <!doctype html> <html lang="en"> <head> <meta http-equiv=&quo ...

  4. Android_ViewPager_实现多个图片水平滚动

    1.示意图                       2.实现分析 (1).xml配置 <!-- 配置container和pager的clipChildren=false, 并且指定margi ...

  5. Android ViewPager实现多个图片水平滚动

    1.示意图                       2.实现分析 (1).xml配置 <!-- 配置container和pager的clipChildren=false, 并且指定margi ...

  6. iOS开发之多图片无缝滚动组件封装与使用

    经常有园友会问"博主,有没有图片无限滚动的Demo呀?", 正儿八经的图片滚动的Demo我这儿还真没有,今天呢就封装一个可以在项目中直接使用的图片轮播.没看过其他iOS图片无限轮播 ...

  7. IOS UIScrollView + UIButton 实现segemet页面和顶部标签页水平滚动效果

    很长一段时间没有写博客了,最近在学习iOS开发,看了不少的代码,自己用UIScrollView和UIButton实现了水平滚动的效果,有点类似于今日头条的主界面框架,效果如下: 代码如下: MyScr ...

  8. ASP.NET中使用JavaScript实现图片自动水平滚动效果

    参照网上的资料,在ASP.NET中使用JavaScript实现图片自动水平滚动效果. 1.页面前台代码: <%@ Page Language="C#" AutoEventWi ...

  9. 使用Recyclerview实现图片水平自动循环滚动

    简介: 本篇博客主要介绍的是如何使用RecyclerView实现图片水平方向自动循环(跑马灯效果) 效果图: 思路: 1.准备m张图片 1.使用Recyclerview实现,返回无数个(实际Inter ...

随机推荐

  1. 用SpringSecurity从零搭建pc项目-01

    注:之前写过一些列的SpringSecurity的文章,重新写一遍是为了把某些不必要的步骤省去,留下精简的,因为工作中有一些不需要. 在java的权限框架里,shiro和SpringSecurity是 ...

  2. ES6快到碗里来---一个简单的爬虫指南

    学习ES6的时候,没少看ES6入门,到现在也就明白了个大概(惭愧脸).这里不谈ES6,只谈怎么把ES6的页面爬下来放到一起成为一个离线文档. 之前居然没注意过作者把这本书开源了..瞎耽误功夫...地址 ...

  3. 读书笔记(03) - 性能 - JavaScript高级程序设计

    作用域链查找 作用域链的查找是逐层向上查找.查找的层次越多,速度越慢.随着硬件性能的提升和浏览器引擎的优化,这个慢我们基本可以忽略. 除了层级查找损耗的问题,变量的修改应只在局部环境进行,尽量避免在局 ...

  4. vue2.0+Element-ui实战案例

    前言 我们将会选择使用一些 vue 周边的库vue-cli, vue-router,axios,moment,Element-ui搭建一个前端项目案例,后端数据接口,会使用json-server快速搭 ...

  5. p132代码解析

    1. long before = System.currentTimeMillis(); long after = System.currentTimeMillis(); //该两句代码规定了一个do ...

  6. Centos下安装最新版Mono并为windwos服务配置开机启动项

    一:安装Mono,此步骤参照官网 1:配置Yum仓库 #Centos 7yum install yum-utils rpm --import "http://keyserver.ubuntu ...

  7. 数据结构与算法(周鹏-未出版)-第六章 树-6.5 Huffman 树

    6.5 Huffman 树 Huffman 树又称最优树,可以用来构造最优编码,用于信息传输.数据压缩等方面,是一类有着广泛应用的二叉树. 6.5.1 二叉编码树 在计算机系统中,符号数据在处理之前首 ...

  8. 【Vue.js实战案例】- Vue.js递归组件实现组织架构树和选人功能

    大家好!先上图看看本次案例的整体效果. 浪奔,浪流,万里涛涛江水永不休.如果在jq时代来实这个功能简直有些噩梦了,但是自从前端思想发展到现在的以MVVM为主流的大背景下,来实现一个这样繁杂的功能简直不 ...

  9. A Mini Locomotive(01背包变型)

    题目链接: https://vjudge.net/problem/POJ-1976 题目描述: A train has a locomotive that pulls the train with i ...

  10. 基于多层感知机的手写数字识别(Tensorflow实现)

    import numpy as np import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_dat ...