自定义 UICollectionViewFlowLayout

2020-03-24  本文已影响0人  easy_luo

UICollectionView 实现各式复杂布局核心在于 UICollectionViewLayout,需要我们去自定义实现。

通过各种layout 的自定义实现,以及它们之间的切换。可以实现一些酷炫的布局,例如

(图片选自:http://www.cnblogs.com/markstray/p/5822262.html

Cover Flow 布局


image.png

堆叠布局

image.png

圆形布局

image.png

关于需要重写方法的描述

自定义布局需要重写以下四个方法

(文字描述取自:https://www.cnblogs.com/wangliang2015/p/5388658.htmlhttps://www.cnblogs.com/hissia/p/5723629.html):

-(CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity;

具体实现逻辑

以 Cover Flow 布局的实现为例子进行阐述

image.png

进行初始化

- (instancetype)init {
    if (self = [super init]) {
        
    }
    return self;
}

进行布局,并且 collectionView 显示 rect 改变是进行布局刷新

- (void)prepareLayout {
    [super prepareLayout];
    
    // 水平滚动
    self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
    
    // 决定第一张图片所在的位置
    CGFloat margin = (self.collectionView.frame.size.width - self.itemSize.width) / 2;
    self.collectionView.contentInset = UIEdgeInsetsMake(0, margin, 0, margin);
}
 
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
    return YES;
} // return YES to cause the collection view to requery the layout for geometry information

该方法是进行自定义的核心,在这里写各种算法以达成想要的效果

该例子中,我们要实现的效果是在滑动的过程中,item 逐渐接近中心,会逐步增大,直到显示最大值,之后不断远离中心点,item 逐步缩小。这一效果类似于三角函数中的正、余弦函数。初步锁定这两个函数,而要选择哪一个?

在滑动的过程中,我们可以得到

self.collectionView.contentOffset.x :这是内容最左侧相对于 collectionView 最左侧的偏移值

attributes.center.x :这是当前 item 的水平中心点,该 x 值是从内容的最左侧算起,直到当前 item 的水平中心点的,全部加起来就是该 item 的水平中心 x。

再者,无论是否滑动,都有一个固定值:

self.collectionView.center.x :位于屏幕水平方向的中心不变,大小不变。

image.png

那么,以第一个item 进行分析,self.collectionView.contentOffset.x 与 self.collectionView.contentOffset.x 的差值与屏幕中心 x 相减的绝对值为item 中心与屏幕中心之间的距离

        ![image.png](https://img.haomeiwen.com/i5745019/b1a39adbc0592197.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


          屏幕中心与item 中心相距为0                                

          ![image.png](https://img.haomeiwen.com/i5745019/65990e9b7b5fb352.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

           屏幕中心与item 中心相距40

由此,我们选择余弦函数进行计算。

- (nullable NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
    
    // 闪屏现象解决参考 :https://blog.csdn.net/u013282507/article/details/53103816
    //扩大控制范围,防止出现闪屏现象
    rect.size.width = rect.size.width + KScreenWidth;
    rect.origin.x = rect.origin.x - KScreenWidth/2;
 
    // 让父类布局好样式
    NSArray *arr = [[NSArray alloc] initWithArray:[super layoutAttributesForElementsInRect:rect] copyItems:YES];
    
    
    for (UICollectionViewLayoutAttributes *attributes in arr) {
        CGFloat scale;
//        scale = 1.0;
        
        // collectionView 的 centerX
        CGFloat centerX = self.collectionView.center.x;
        CGFloat step = ABS(centerX - (attributes.center.x - self.collectionView.contentOffset.x));
        NSLog(@"step %@ : attX %@ - offset %@", @(step), @(attributes.center.x), @(self.collectionView.contentOffset.x));
        scale = fabsf(cosf(step/centerX * M_PI/5));
        
        attributes.transform = CGAffineTransformMakeScale(scale, scale);
    }
    
    return arr;
} // return an array layout attributes instances for all the views in the given rect

确保在滚动结束的时候的显示效果,此处确保某一个item 滚动结束时是居中显示的。

- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity {
    
    // 保证滚动结束后视图的显示效果
    
    // 计算出最终显示的矩形框
    CGRect rect;
    rect.origin.y = 0;
    rect.origin.x = proposedContentOffset.x;
    rect.size = self.collectionView.frame.size;
 
    // 获得 super 已经计算好的布局的属性
    NSArray *arr = [super layoutAttributesForElementsInRect:rect];
 
    // 计算 collectionView 最中心点的 x 值
    CGFloat centerX = proposedContentOffset.x + self.collectionView.frame.size.width * 0.5;
 
    CGFloat minDelta = MAXFLOAT;
    for (UICollectionViewLayoutAttributes *attrs in arr) {
        if (ABS(minDelta) > ABS(attrs.center.x - centerX)) {
            minDelta = attrs.center.x - centerX;
        }
    }
 
    proposedContentOffset.x += minDelta;
    
    return proposedContentOffset;
} // return a point at which to rest after scrolling - for layouts that want snap-to-point scrolling behavior
代码地址:https://download.csdn.net/download/u013410274/10356732

所有的参考链接:

WWDC 2012 Session 笔记 -- 205 Introducing Collection Views

http://www.cnblogs.com/markstray/p/5822262.html

UICollectionViewLayout 继承 UICollectionViewFlowLayout 自定义布局

https://www.cnblogs.com/wangliang2015/p/5388658.html

自定义流水布局(UICollectionViewFlowLayout 的基本使用)

https://www.cnblogs.com/hissia/p/5723629.html

iOS 利用余弦函数实现卡片浏览工具

https://blog.csdn.net/u013282507/article/details/53103816
————————————————
版权声明:本文为CSDN博主「若歆」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u013410274/article/details/79925531

上一篇下一篇

猜你喜欢

热点阅读