自定义UICollectionView布局
发布日期:2022-03-18 08:27:47 浏览次数:23 分类:技术文章

本文共 8233 字,大约阅读时间需要 27 分钟。

记录ray上学习自定义UICollectionView布局学习笔记

自定义UICollectionView布局

UICollectionViewFlowLayout

UICollectionViewFlowLayoutUICollectionView的默认的布局方式

UICollectionViewDelegateFlowLayout代理方法

1.func collectionView(_:layout:sizeForItemAt:) -> CGSizeitem的size

2.func collectionView(_:layout:insetForSectionAt:) -> UIEdgeInsetssection的inset,相当于是内容的margin
3.func collectionView(_:layout:minimumLineSpacingForSectionAt:) -> CGFloat行间距
4.func collectionView(_:layout:minimumInteritemSpacingForSectionAt:) -> CGFloatitem之间的距离

Carousel布局的实现

1.最开始的布局,就是一个FlowLayout,其效果如下:

这里写图片描述

2.根据item中心距离collection view中距离,调整item的alpha和scale,其计算的原理大致如下:

这里写图片描述

调整了alpha和scale后,其显示结果如下:

这里写图片描述

3.设置item之间的间距为负值,并设置布局的attribute的zIndex,使其看起来有层次,最中间的item在最上层

这里写图片描述

继承UICollectionViewFlowLayout

继承UICollectionViewFlowLayout创建一个类似于carousel的布局。重写一些关键方法。

本例主要实现过程:

1.继承UICollectionViewFlowLayout
2.变换cell的scale和alpha的值
3.设置line spacing,这样item就可以重叠
4.修改cell的形状,添加一个border
5.让cell居中

1.重写override func layoutAttributesForElements(in:) -> [UICollectionViewLayoutAttributes]方法,根据距离view中心的距离,调整scale和alpha

2.重写override func shouldInvalidateLayout(forBoundsChange:) -> Bool方法,来更新布局
3.重写override func targetContentOffset(forProposedContentOffset:withScrollingVelocity:) -> CGPoint方法,返回滚动停止的位置
4.要注意的是第一个item和最后一个item也可以居中显示,所以要调整sectionInset

自定义的CharacterFlowLayout

class CharacterFlowLayout: UICollectionViewFlowLayout {    var standardItemAlpha: CGFloat = 0.5    var standardItemScale: CGFloat = 0.5    var isSetup = false    //During each layout update, the collection view calls this method first to give your layout object a chance to prepare for the upcoming layout operation.    override func prepare() {        super.prepare()        if isSetup == false {            setupCollectionView()            isSetup = true        }    }    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {        let attributes = super.layoutAttributesForElements(in: rect)        var attributesCopy = [UICollectionViewLayoutAttributes]()        for itemAttributes in attributes! {            let itemAttributesCopy = itemAttributes.copy() as! UICollectionViewLayoutAttributes            changeLayoutAttributes(itemAttributesCopy)            attributesCopy.append(itemAttributesCopy)        }        return attributesCopy    }    override open func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {        return true    }    //改变布局属性    func changeLayoutAttributes(_ attributes: UICollectionViewLayoutAttributes) {        let collectionCenter = collectionView!.frame.size.height/2        let offset = collectionView!.contentOffset.y        //相当于item中心点距离屏幕的距离        let normalizedCenter = attributes.center.y - offset        //最大的distance: 上一个item高度一半+lineSpacing+下一个item高度一半        let maxDistance = self.itemSize.height + self.minimumLineSpacing        //item的中心点距离屏幕中心的距离        let distance = min(abs(collectionCenter - normalizedCenter), maxDistance)        let ratio = (maxDistance - distance)/maxDistance        //调整alpha和scale和zIndex        let alpha = ratio * (1 - self.standardItemAlpha) + self.standardItemAlpha        let scale = ratio * (1 - self.standardItemScale) + self.standardItemScale        attributes.alpha = alpha        attributes.transform3D = CATransform3DScale(CATransform3DIdentity, scale, scale, 1)        attributes.zIndex = Int(alpha * 10)    }    //Returns the point at which to stop scrolling.    override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {        print("\(proposedContentOffset.x) : \(proposedContentOffset.y)")        let layoutAttributes = self.layoutAttributesForElements(in: collectionView!.bounds)        let center = collectionView!.bounds.size.height / 2        let proposedContentOffsetCenterOrigin = proposedContentOffset.y + center        //获取距离中心最近item的layoutAttributes        let closest = layoutAttributes!.sorted { abs($0.center.y - proposedContentOffsetCenterOrigin) < abs($1.center.y - proposedContentOffsetCenterOrigin) }.first ?? UICollectionViewLayoutAttributes()        let targetContentOffset = CGPoint(x: proposedContentOffset.x, y: floor(closest.center.y - center))        return targetContentOffset    }    //主要是设置sectionInset    func setupCollectionView() {        self.collectionView!.decelerationRate = UIScrollViewDecelerationRateFast        let collectionSize = collectionView!.bounds.size        let yInset = (collectionSize.height - self.itemSize.height) / 2        let xInset = (collectionSize.width - self.itemSize.width) / 2        self.sectionInset = UIEdgeInsetsMake(yInset, xInset, yInset, xInset)    }}

效果如下:

旋转木马的效果

可拉伸的Header

继承UICollectionViewFlowLayout,重写override func layoutAttributesForElements(in:) -> [UICollectionViewLayoutAttributes]方法,通过检查representedElementKind判断是否为section header,然后改变section header的高度

class StretchyHeaderLayout: UICollectionViewFlowLayout {  override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {    let layoutAttributes = super.layoutAttributesForElements(in: rect)! as [UICollectionViewLayoutAttributes]    let offset = collectionView!.contentOffset    if (offset.y < 0) {      let deltaY = fabs(offset.y)      for attributes in layoutAttributes {        if let elementKind = attributes.representedElementKind {          if elementKind == UICollectionElementKindSectionHeader {            var frame = attributes.frame            frame.size.height = max(0, headerReferenceSize.height + deltaY)            frame.origin.y = frame.minY - deltaY            attributes.frame = frame          }        }      }    }    return layoutAttributes  }  override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {    return true  }}

效果如下:

这里写图片描述

UICollectionViewLayout

UICollectionViewLayout是一个抽象的基类。

在继承UICollectionViewLayout之前,首先要考虑的是使用UICollectionViewFlowLayout
下列情形可以考虑使用UICollectionViewLayout
1.不是网格布局
2.需要在多个方向滚动
3.cell的位置频繁的改变

HOW UICOLLECTIONVIEWLAYOUT WORKS

  • Handful of methods provide core behavior
  • Size of the scrollable content area
  • Attributes for cells and views position
  • Collection view works with custom layout to manage layout process

在布局的过程中,collectionView调用你布局对象的三个方法的顺序如下:

1.override func prepare()执行初始的计算
2.override var collectionViewContentSize : CGSize返回整个content区域的大小
3.override func layoutAttributesForElements(in:) -> [UICollectionViewLayoutAttributes]指定矩形区域内cell和view的attribute的集合

如下的多行布局,使用了UICollectionViewLayout

class CustomViewLayout: UICollectionViewLayout {    var numberOfColumns = 0    var cache = [UICollectionViewLayoutAttributes]()    fileprivate var contentHeight: CGFloat = 0    fileprivate let cellHeight: CGFloat = 300.0    fileprivate var width: CGFloat {        get{            return collectionView!.bounds.width        }    }    override func prepare() {        if cache.isEmpty {            let columnWidth = width / CGFloat(numberOfColumns)            var xOffsets = [CGFloat]()//x方向的偏移量            for column in 0..
= (numberOfColumns - 1) ? 0 : column+1 } } } override var collectionViewContentSize: CGSize{ return CGSize(width: width, height: contentHeight) } override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { var layoutAttributes = [UICollectionViewLayoutAttributes]() for attributes in cache { if attributes.frame.intersects(rect) {//rect相交 layoutAttributes.append(attributes) } } return layoutAttributes }}

效果如下:

多行布局

多个collection view布局

步骤:

1.创建layout
2.配置layout
3.使当前layout无效
4.设置layout

@IBAction func carouselLayoutButtonAction(_ sender: UIBarButtonItem) {    let layout = CarouselViewLayout()    layout.delegate = self    layout.numberOfColumns = 1    layout.cellPadding = -20    collectionView!.contentInset = UIEdgeInsets(top: 5, left: 100, bottom: 10, right: 100)    collectionView?.collectionViewLayout.invalidateLayout()    collectionView?.setCollectionViewLayout(layout, animated: true)    collectionView?.reloadData()  }

注意重写layoutAttributesForItem方法

override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {    return cache[indexPath.item]  }

效果如下:

这里写图片描述

参考

转载地址:https://windzen.blog.csdn.net/article/details/70195024 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:UIViewController相关
下一篇:Mosaic布局(类似于瀑布流)

发表评论

最新留言

哈哈,博客排版真的漂亮呢~
[***.90.31.176]2024年03月02日 15时06分25秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章

php pclzip.lib.php,php使用pclzip类实现文件压缩的方法(附pclzip类下载地址) 2019-04-21
php dns更新,php_mzdns: 站群,大量域名 通过 dns 服务商 api 批量添加 ip 工具。你懂的~ 基于 mzphp2 框架。... 2019-04-21
jdk 1.8 java.policy,JDK1.8 导致系统报错:java.security.InvalidKeyException:illegal Key Size 2019-04-21
php linux权限,Linux权限详细介绍 2019-04-21
典型环节的matlab仿真分析,典型环节的MATLAB仿真.doc 2019-04-21
Php contenttype类型,各种类型文件的Content Type 2019-04-21
php使用redis持久化,redis如何持久化 2019-04-21
php7.1解压包安装,【Swoole】php7.1安装swoole扩展 2019-04-21
linux centos删除安装的包,CentOS yum认为已删除的软件包仍在安装中 2019-04-21
酒店管理系统c语言带注释,酒店管理系统--C语言版.pdf 2019-04-21
c语言 实现sizeof功能,C语言简单实现sizeof功能代码 2019-04-21
c语言sin函数近似值,用泰勒公式求sin(x)的近似值 2019-04-21
c 语言登录系统源代码,c语言源代码---------------个人图书管理系统 2019-04-21
android线程通信方式,Android 主线程和子线程通信问题 2019-04-21
在线设计 html5 表单,html5注册表单制作-表单制作-小程序表单制作 2019-04-21
android小闹钟课程设计,《小闹钟》教学设计 2021-06-24
mysql文件系统_MySQL文件系统先睹为快(1) 2021-06-24
nums在python_程序找到一对(i,j),其中nums [i] + nums [j] +(i -j)在Python中最大化?... 2021-06-24
jquery后台内容管理_教育平台项目后台管理系统:课程内容模块 2021-06-24
grouping函数 mysql_sql聚合函数有哪些 2019-04-21