Swift 自定义 UICollectionViewFlowLayout 实现横向布局分页
2017-10-15 23:28
691 查看
需求
在当前的移动端开发中,我们经常可以看见集合视图需要横向布局分页的场景。例如:微信的表情
美团外卖的首页
需求实现分析
//预布局方法 布局相关代码可放在此处 override func prepare() { }
/** 返回true只要显示的边界发生改变就重新布局:(默认是false) 内部会重新调用prepareLayout和调用 layoutAttributesForElementsInRect方法获得部分cell的布局属性 */ override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool { return true; }
/* 根据indexPath去对应的UICollectionViewLayoutAttributes 这个是取值的,要重写,在移动删除的时候系统会调用改方法重新去UICollectionViewLayoutAttributes然后布局 */ override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? { return super.layoutAttributesForItem(at: indexPath); }
//返回当前的ContentSize override open var collectionViewContentSize: CGSize { return CGSize(width: (self.collectionView?.width)! * CGFloat(self.collectionView!.numberOfSections), height: self.collectionView!.height); }
// 返回所有的布局属性 override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { return super.layoutAttributesForElements(in: rect); }
布局代码
import UIKit
class EmjoyCollectionViewLayout: UICollectionViewFlowLayout {
var numRow:Int = 0;//行数
var numCol:Int = 0;//列数
var contentInsets: UIEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 0)
//所有cell的布局属性
var layoutAttributes: [UICollectionViewLayoutAttributes] = [UICollectionViewLayoutAttributes]();
override init() {
super.init();
self.itemSize = CGSize(width: 50, height: 50);
self.scrollDirection = .horizontal
self.numRow = 7;
self.numCol = 4;
self.contentInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
//计算布局
override func prepare() {
let numsection = self.collectionView!.numberOfSections;
let itemNum: Int = self.collectionView!.numberOfItems(inSection: 0)
layoutAttributes.removeAll();
for i in 0..<numsection{
for j in 0..<itemNum{
let layout = self.layoutAttributesForItem(at: IndexPath(item: j, section: i))!;
self.layoutAttributes.append(layout);
}
}
}
/** 返回true只要显示的边界发生改变就重新布局:(默认是false) 内部会重新调用prepareLayout和调用 layoutAttributesForElementsInRect方法获得部分cell的布局属性 */ override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool { return true; }
/*
根据indexPath去对应的UICollectionViewLayoutAttributes 这个是取值的,要重写,在移动删除的时候系统会调用改方法重新去UICollectionViewLayoutAttributes然后布局
*/
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
assert((self.collectionView!.width-self.contentInsets.left - self.contentInsets.right) >= (self.itemSize.width*CGFloat(self.numRow)), "布局宽度不能超过父试图宽度")
assert((self.collectionView!.height-self.contentInsets.top - self.contentInsets.bottom) >= self.itemSize.height*CGFloat(self.numCol), "布局高度不能超过父视图高度")
let layoutAttribute = super.layoutAttributesForItem(at: indexPath);
//计算水平距离
let hor_spacing = (self.collectionView!.width - CGFloat(self.numRow) * self.itemSize.width - self.contentInsets.left - self.contentInsets.right) / CGFloat(self.numRow - 1);
//计算垂直距离
let ver_spacing = (self.collectionView!.height - CGFloat(self.numCol) * self.itemSize.height - self.contentInsets.top - self.contentInsets.bottom) / CGFloat(self.numCol - 1);
//计算x的位置
var frame_x = CGFloat(indexPath.section) * self.collectionView!.width + CGFloat(indexPath.row%self.numRow) * self.itemSize.width + self.contentInsets.left;
frame_x += (hor_spacing*(CGFloat(indexPath.row%self.numRow)));
//计算y的位置
var frame_y = CGFloat((indexPath.row/self.numRow)) * self.itemSize.height + self.contentInsets.top;
frame_y += (ver_spacing*CGFloat(indexPath.row/self.numRow));
layoutAttribute?.frame = CGRect(x:frame_x, y:frame_y, width: self.itemSize.width, height: self.itemSize.height);
return layoutAttribute;
}
override open var collectionViewContentSize: CGSize {
return CGSize(width: (self.collectionView?.width)! * CGFloat(self.collectionView!.numberOfSections), height: self.collectionView!.height);
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
return self.layoutAttributes
}
}
使用示例部分代码
let collectionView = UICollectionView(frame: M_RECT(0, y: NEWHEIGHT(0), w: kScreenW, h: kScreenH - 64 - NEWHEIGHT(0)), collectionViewLayout: EmjoyCollectionViewLayout()); collectionView.delegate = self; collectionView.dataSource = self;
/** 返回每个分组有几个数据 */ func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int{ return 32; } /** 返回有几个分组 */ func numberOfSections(in collectionView: UICollectionView) -> Int { return 2; }
在实际使用的过程中 布局的行数、列数等均可以根据实际需求自定义。
效果如下:
相关文章推荐
- iOS - UICollectionView 自定义布局之风火轮[译] (原版为swift,我这里的实现改为OC)
- Swift - 使用网格(UICollectionView)的自定义布局实现复杂页面
- Swift - 使用网格(UICollectionView)的自定义布局实现复杂页面
- Swift - 使用网格(UICollectionView)的自定义布局实现复杂页面
- iOS - Swift UICollectionView横向分页滚动,cell左右排版
- swift:自定义UICollectionViewFlowLayout
- IOS中通过UICollectionView和UICollectionViewFlowLayout设置初始游标cursor来控制左右滑动来实现多条记录水平分页显示及控制音标读音
- iOS Swift UICollectionView横向分页滚动,cell左右排版问题详解
- iOS自定义UICollectionViewLayout布局实现瀑布流
- iOS使用EGO实现UITableView或UICollectionView横向分页刷新
- iOS自定义UICollectionViewLayout实现瀑布流布局
- 横向分页滚动的UICollectionView,cell左右排版 支持多组Cell实现。
- 源码推荐(12.01B):一行代码搞定自动布局,自定义UICollectionViewFlowLayout
- 自定义UICollectionViewFlowLayout进行瀑布流布局
- swift:自定义UICollectionViewFlowLayout
- Android自定义ViewGroup实现可滚动的横向布局(2)
- 可自定义分页宽度的UIScrollView(Swift实现)
- IOS瀑布流 通过自定义UICollectionViewController的Layout布局实现
- Android 自定义ViewGroup 实现FlowLayout,动态添加布局
- iOS - Swift UICollectionView横向分页的问题