Swift 自定义 UICollectionViewFlowLayout 实现横向布局分页

需求

在当前的移动端开发中,我们经常可以看见集合视图需要横向布局分页的场景。例如:

微信的表情

Swift 自定义 UICollectionViewFlowLayout 实现横向布局分页_第1张图片

美团外卖的首页

Swift 自定义 UICollectionViewFlowLayout 实现横向布局分页_第2张图片

需求实现分析

//预布局方法 布局相关代码可放在此处
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..in 0...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;
    }

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

效果如下:

你可能感兴趣的:(Swift,iOS)