最近需求中需要用到中间有间隔的无限轮播图,中间还要放大。所以仿照SDCycleScrollView的原理自己写了一个无限轮播图。
QQ20180305-203421.gifSDCycleScrollView实现思想
利用UICollectionView的Cell重用机制,完美解决了创建多个UIImageView的性能问题。首先创建一个UICollectionView,然后创建根据图片的个数创建item,如果有4张图片则创建4*100个item,也就是100组图片,然后定位初始位置在中间那组的一个。当滑动到最后一个item的时候把offset再移动到中间。
当然这种方法只是实现了自动轮播的无限,如果是手动滑动还是可以滑到尽头的,不过相信很少人会这么无聊滑动到最后。
下面看一下核心逻辑代码。
初始化collectionView的时候如果是无限循环则把item滑动到中间
if(self.collectionView.contentOffset.x == 0 && _totalItems > 0)
{
NSInteger targeIndex = 0;
if(self.infiniteLoop)
{//无线循环
// 如果是无限循环,应该默认把 collection 的 item 滑动到 中间位置。
// 注意:此处 totalItems 的数值,其实是图片数组数量的 100 倍。
// 乘以 0.5 ,正好是取得中间位置的 item 。图片也恰好是图片数组里面的第 0 个。
targeIndex = _totalItems * 0.5;
}else
{
targeIndex = 0;
}
//设置图片默认位置
[self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:targeIndex inSection:0] atScrollPosition:UICollectionViewScrollPositionNone animated:NO];
}
}
计算当前index的逻辑
if (_mainView.sd_width == 0 || _mainView.sd_height == 0) {
return 0;
}
int index = 0;
if (_flowLayout.scrollDirection == UICollectionViewScrollDirectionHorizontal) {
index = (_mainView.contentOffset.x + _flowLayout.itemSize.width * 0.5) / _flowLayout.itemSize.width;
} else {
index = (_mainView.contentOffset.y + _flowLayout.itemSize.height * 0.5) / _flowLayout.itemSize.height;
}
return MAX(0, index);
轮播图中间添加间距
怎么让轮播图中间有间距呢?因为每个图片是一个cell。cell默认的宽度是屏幕宽度,所以我们只需要把cell的宽度设置小一点,然后在加上间距就可以了。
-(void)setItemWidth:(CGFloat)itemWidth
{
_itemWidth = itemWidth;
self.flowLayout.itemSize = CGSizeMake(itemWidth, self.bounds.size.height);
}
-(void)setItemSpace:(CGFloat)itemSpace
{
_itemSpace = itemSpace;
self.flowLayout.minimumLineSpacing = itemSpace;
}
然后设置图片默认位置,注意移动方式的参数是UICollectionViewScrollPositionCenteredHorizontally
表示横向移动将cell移到屏幕中间
[self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:targeIndex inSection:0] atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:NO];
然后计算当前index的逻辑需要修改,以前的一页是cell的宽度,现在因为有间距了所以一页是cell的宽度加上cell间的空隙
-(NSInteger)currentIndex
{
if(self.collectionView.frame.size.width == 0 || self.collectionView.frame.size.height == 0)
return 0;
NSInteger index = 0;
if (_flowLayout.scrollDirection == UICollectionViewScrollDirectionHorizontal) {//水平滑动
index = (self.collectionView.contentOffset.x + (self.itemWidth + self.itemSpace) * 0.5) / (self.itemSpace + self.itemWidth);
}else{
index = (self.collectionView.contentOffset.y + _flowLayout.itemSize.height * 0.5)/ _flowLayout.itemSize.height;
}
return MAX(0,index);
}
最后就是手动滑动的时候,位移一个cell宽度加间隙的距离,其实只要算对了index,直接位移过去就好。
//手离开屏幕的时候
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset{
//如果是向右滑或者滑动距离大于item的一半,则像右移动一个item+space的距离,反之向左
float currentPoint = scrollView.contentOffset.x;
float moveWidth = currentPoint-_oldPoint;
int shouldPage = moveWidth/(self.itemWidth/2);
if (velocity.x>0 || shouldPage > 0) {
_dragDirection = 1;
}else if (velocity.x<0 || shouldPage < 0){
_dragDirection = -1;
}else{
_dragDirection = 0;
}
}
//松开手指滑动开始减速的时候,设置滑动动画
- (void)scrollViewWillBeginDecelerating: (UIScrollView *)scrollView{
NSInteger currentIndex = (_oldPoint + (self.itemWidth + self.itemSpace) * 0.5) / (self.itemSpace + self.itemWidth);
[self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:currentIndex + _dragDirection inSection:0] atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];
}
这样有间距的无限轮播就完成,但是需求中还要中间的图片放大,两边的图片缩小,滑动的时候,滑到中间是最大的。
其实这个也比较简单,只要根据cell距离屏幕中间多远来设置cell缩放的大小,中间最大,两边最小。在哪里设置呢,这里要继承UICollectionViewFlowLayout
,然后重写layoutAttributesForElementsInRect
方法
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
// 1.获取cell对应的attributes对象
NSArray* arrayAttrs = [[NSArray alloc] initWithArray:[super layoutAttributesForElementsInRect:rect] copyItems:YES];
if(!self.isZoom) return arrayAttrs;
// 2.计算整体的中心点的x值
CGFloat centerX = self.collectionView.contentOffset.x + self.collectionView.bounds.size.width * 0.5;
// 3.修改一下attributes对象
for (UICollectionViewLayoutAttributes *attr in arrayAttrs) {
// 3.1 计算每个cell的中心点距离
CGFloat distance = ABS(attr.center.x - centerX);
// 3.2 距离越大,缩放比越小,距离越小,缩放比越大
CGFloat factor = 0.001;
CGFloat scale = 1 / (1 + distance * factor);
attr.transform = CGAffineTransformMakeScale(scale, scale);
}
return arrayAttrs;
}
这样就搞定了。是不是很简单。不废话了直接给项目
网友评论