您的位置:首页 > 移动开发 > Swift

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;
}


在实际使用的过程中 布局的行数、列数等均可以根据实际需求自定义。

效果如下:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: