
本文共 8233 字,大约阅读时间需要 27 分钟。
记录ray上学习自定义UICollectionView布局学习笔记
自定义UICollectionView布局
UICollectionViewFlowLayout
UICollectionViewFlowLayout
是UICollectionView
的默认的布局方式
UICollectionViewDelegateFlowLayout
代理方法
1.func collectionView(_:layout:sizeForItemAt:) -> CGSize
item的size
func collectionView(_:layout:insetForSectionAt:) -> UIEdgeInsets
section的inset,相当于是内容的margin 3.func collectionView(_:layout:minimumLineSpacingForSectionAt:) -> CGFloat
行间距 4.func collectionView(_:layout:minimumInteritemSpacingForSectionAt:) -> CGFloat
item之间的距离 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
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 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!
发表评论
最新留言
关于作者
