实现这个功能主要分步实现两个内容,首先视图横向滚动放大,然后实现左右的循环滚动。
一、横向居中放大滚动
在自定义UICollectionViewFlowLayout
设置滚动动画,动画的使用参考文章中的第一篇参考文章
首先自定义一个UICollectionViewFlowLayout
//设置放大动画
-(NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect
{
NSArray *arr = [self getCopyOfAttributes:[super layoutAttributesForElementsInRect:rect]];
//屏幕中线
CGFloat centerX = self.collectionView.contentOffset.x + self.collectionView.bounds.size.width/2.0f;
//刷新cell缩放
for (UICollectionViewLayoutAttributes *attributes in arr) {
CGFloat distance = fabs(attributes.center.x - centerX);
//移动的距离和屏幕宽度的的比例
CGFloat apartScale = distance/self.collectionView.bounds.size.width;
//把卡片移动范围固定到 -π/4到 +π/4这一个范围内
CGFloat scale = fabs(cos(apartScale * M_PI/4));
//设置cell的缩放 按照余弦函数曲线 越居中越趋近于1
attributes.transform = CGAffineTransformMakeScale(1.0, scale);
}
return arr;
}
//防止报错 先复制attributes
- (NSArray *)getCopyOfAttributes:(NSArray *)attributes
{
NSMutableArray *copyArr = [NSMutableArray new];
for (UICollectionViewLayoutAttributes *attribute in attributes) {
[copyArr addObject:[attribute copy]];
}
return copyArr;
}
//是否需要重新计算布局
-(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
return true;
}
通过该CollectionViewFlowLayout创建collectionView
-(void)createUI{
JLXUICollectionViewFlowLayout *layout = [[JLXUICollectionViewFlowLayout alloc] init];
layout.itemSize = CGSizeMake(474*kJLXWidthScale, 848*kJLXHeightScale);
layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
layout.minimumLineSpacing = 50*kJLXWidthScale;
layout.minimumInteritemSpacing = 50*kJLXWidthScale;
self.collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
[self.collectionView setBackgroundColor:kJLXBackgroundColor];
self.collectionView.delegate = self;
self.collectionView.dataSource = self;
self.collectionView.pagingEnabled = NO;
self.collectionView.showsHorizontalScrollIndicator = NO;
[self.view addSubview:self.collectionView];
[self.collectionView registerClass:[HomeCaseCell class] forCellWithReuseIdentifier:reuseIdentifier];
[self.view addSubview:self.collectionView];
[self.collectionView setFrame:CGRectMake(0, 40*kJLXHeightScale, JLXScreenSize.width, JLXScreenSize.height)];
self.collectionView.contentSize = CGSizeMake(self.caseArray.count*JLXScreenSize.width, 0);
}
因为要调整cell的边距缩进,所以我们在代理方法中修改了collection的EdgeInsets
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
//自定义item的UIEdgeInsets
return UIEdgeInsetsMake(0, self.view.bounds.size.width/2.0-474*kJLXWidthScale/2, 0, self.view.bounds.size.width/2.0-474*kJLXWidthScale/2);
}
然后在拖动开始和结束时记录滚动的距离,调整居中
//手指拖动开始
-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
self.m_dragStartX = scrollView.contentOffset.x;
}
//手指拖动停止
-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
self.m_dragEndX = scrollView.contentOffset.x;
dispatch_async(dispatch_get_main_queue(), ^{
[self fixCellToCenter];
});
}
//配置cell居中
- (void)fixCellToCenter {
//最小滚动距离
float dragMiniDistance = self.view.bounds.size.width/20.0f;
if (self.m_dragStartX - self.m_dragEndX >= dragMiniDistance) {
self.m_currentIndex -= 1;//向右
}else if(self.m_dragEndX - self.m_dragStartX >= dragMiniDistance){
self.m_currentIndex += 1;//向左
}
NSInteger maxIndex = [_collectionView numberOfItemsInSection:0] - 1;
self.m_currentIndex = self.m_currentIndex <= 0 ? 0 : self.m_currentIndex;
self.m_currentIndex = self.m_currentIndex >= maxIndex ? maxIndex : self.m_currentIndex;
[_collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:self.m_currentIndex inSection:0] atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];
}
二、实现循环滚动
看了几个循环滚动的文章,比如第三篇参考文章,发现并不适合现在的情况,因为现存的几乎全是cell铺满整个屏幕,而现在的这个是半屏放大的,所以不能采用
在-(void)scrollViewDidEndDecelerating:(UIScrollview *)scrollview函数中通过scrollView 的偏移量来判断是否到达两个边界,再分别进行处理这个方案。
所以需要使用其他方案知道当前的cell的indexpath,通过参考第四个参考文章获取到了indexpath,但是在
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
// 将collectionView在控制器view的中心点转化成collectionView上的坐标
CGPoint pInView = [self.view convertPoint:self.collectionView.center toView:self.collectionView];
// 获取这一点的indexPath
NSIndexPath *indexPathNow = [self.collectionView indexPathForItemAtPoint:pInView];
// 赋值给记录当前坐标的变量
self.currentIndexPath = indexPathNow;
// 更新底部的数据
// ...
}
获取的时候并不准确,比如我没有采用动画,而是手指一直拖动cell的话,这个函数就不会调用,所以索性就使用了m_currentIndex来记录indexpath。
因为是左右滚动,并且不是全屏的,所以在初始化的时候数组的对象循环放四次,这时候数组中存放的对象如下图所示,初始位置放在下图中B的位置
-(void)loadData{
NSArray *array = [NSArray arrayWithObjects:@"img1",@"img2",@"img3",@"img4", nil];
self.caseArray = [NSMutableArray array];
///加四次为了循环
for (int i=0; i<4; i++) {
[self.caseArray addObjectsFromArray:array];
}
[self.collectionView reloadData];
[self.collectionView layoutIfNeeded];
[self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:self.caseArray.count/2 inSection:0] atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:NO];
self.m_currentIndex = self.caseArray.count/2;
}
这样存放的意义就是左右都是有图片的,当往右滚动到C的位置的时候,将collectionview的位置恢复到B,当往左滚动到A的位置的时候,也将collectionview的位置恢复到B,这样就不会出现左右边界空白的情况了。
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
{
if (self.m_currentIndex == [self.caseArray count]/4*3) {
NSIndexPath *path = [NSIndexPath indexPathForItem:[self.caseArray count]/2 inSection:0];
[self.collectionView scrollToItemAtIndexPath:path atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:NO];
self.m_currentIndex = [self.caseArray count]/2;
}
else if(self.m_currentIndex == [self.caseArray count]/4){
NSIndexPath *path = [NSIndexPath indexPathForItem:[self.caseArray count]/2 inSection:0];
[self.collectionView scrollToItemAtIndexPath:path atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:NO];
self.m_currentIndex = [self.caseArray count]/2;
}
}
效果动图演示
Demo下载
Github下载:https://github.com/DamonHu/CollectionCircleDemo
Gitosc下载:http://git.oschina.net/DamonHoo/CollectionCircleDemo
参考文章
- iOS 利用UICollectionView横向滚动、余弦函数曲线特性实现居中放大的卡片浏览工具 XLCardSwitch
- iOS 利用余弦函数实现卡片浏览工具
- iOS无限轮播图片的两种方式
- 获取CollectionViewCell的indexPath
版权属于:东哥笔记 - DongGe.me
本文链接:https://dongge.org/blog/557.html
自2017年12月26日起,『转载以及大段采集进行后续编辑』须注明本文标题和链接!否则禁止所有转载和采集行为!
4 条评论
https://blog.csdn.net/weixin_42384267/article/details/111924388
这个人复制你的文章,还声称原创。
感谢您的支持,已对侵权文章投诉通过,该文章已被删除
你好,请问如何修改成将中间放大的那个item显示在最左边
可以在查看参考文章的第一个链接,修改
layoutAttributesForElementsInRect
这个函数的放大布局