UICollectionViewCell間距修改

原文作者:艾歐艾斯之手
原文鏈接:http://www.itdecent.cn/p/2ffca6f93c1c

我們都知道UICollectionViewFlowLayout有一個minimumInteritemSpacing屬性可以控制cell之間水平的間距,但是這個屬性并不是你設置成多少它的間距一定是多少,從這個單詞的字面意思就可以看出來它指的是cell之間的最小間距。也就是說cell的間距是大大于或者等于這個屬性的。于是乎,最近就碰到了測試丟過來的問題-UICollectionViewCell之間的布局。

問題重現(xiàn)

關鍵代碼:

let layout = UICollectionViewFlowLayout()
layout.minimumLineSpacing = 8.0
layout.minimumInteritemSpacing = 8.0
layout.sectionInset = UIEdgeInsets(top: 25, left: 15, bottom: 30, right: 15)
let collection = UICollectionView(frame: self.view.bounds, collectionViewLayout: layout)
collection.backgroundColor = UIColor.white
collection.delegate = self
collection.dataSource = self
collection.register(UINib(nibName: "CollectionCell", bundle: Bundle.main),forCellWithReuseIdentifier: "CollectionCell")
self.view.addSubview(collection)

以上關鍵代碼是layout.minimumInteritemSpacing = 8.0,把cell間距設置成8之后,本以為就可以高枕無憂了,但是載入數(shù)據(jù)之后數(shù)據(jù)效果如下圖:

如圖中紅框內(nèi)的間距明顯不是我們想要。

明確需求

現(xiàn)在想要達到的效果是,cell全部向左對齊,間距一定要控制在8.0,不夠就換一行顯示。

如何解決

要達到這樣的效果就只能是修改布局了。先來看一下布局的實現(xiàn):

//
//  DVMaximumSpacingLayout.swift
//  Amall
//  一個可以設置cell之間最大間距的布局,用于商品詳情的屬性
//  Created by David Yu on 2018/4/26.
//  Copyright ? 2018年 David. All rights reserved.
//

import UIKit

// MARK:-   DVMaximumSpacingLayout代理
@objc protocol DVMaximumSpacingLayoutDelegate {

    func collectionView(_ collectionView: UICollectionView?, layout collectionViewLayout: DVMaximumSpacingLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
    @objc optional func collectionView(_ collectionView: UICollectionView?, layout collectionViewLayout: DVMaximumSpacingLayout, referenceSizeForHeaderInSection section: Int) -> CGSize
    @objc optional func collectionView(_ collectionView: UICollectionView?, layout collectionViewLayout: DVMaximumSpacingLayout, referenceSizeForFooterInSection section: Int) -> CGSize

}

class DVMaximumSpacingLayout: UICollectionViewLayout {

    /// cell最大水平間距
    var MaximumSpacing: CGFloat = 0.0
    /// cell豎直間距
    var minimumLineSpacingForSection: CGFloat = 0.0
    /// 間距
    var sectionEdgeInsets: UIEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
    /// cell布局
    var cellAttributes = [IndexPath: UICollectionViewLayoutAttributes]()
    /// 頭視圖布局
    var headerAttributes = [IndexPath: UICollectionViewLayoutAttributes]()
    /// 尾視圖布局
    var footerAttributes = [IndexPath: UICollectionViewLayoutAttributes]()
    /// 代理
    var delegate: DVMaximumSpacingLayoutDelegate?
    /// 當前Y坐標
    var currentY : CGFloat = 0

    // MARK:- prepareLayout是一個必須要實現(xiàn)的方法,該方法的功能是為布局提供一些必要的初始化參數(shù)
    override func prepare() {
        super.prepare()
        cellAttributes.removeAll()
        headerAttributes.removeAll()
        footerAttributes.removeAll()
        currentY = 0

        //  一共有多少section
        let sectionNum = self.collectionView?.numberOfSections ?? 0
        for i in 0..<sectionNum {
            let supplementaryViewIndex = IndexPath(row: 0, section: i)
            //  計算設置每個header的布局對象
            let headerAttribute = UICollectionViewLayoutAttributes(forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, with: supplementaryViewIndex)
            let headerSize = delegate?.collectionView?(collectionView, layout: self, referenceSizeForHeaderInSection: i) ?? CGSize(width: 0, height: 0)
            headerAttribute.frame = CGRect(origin: CGPoint(x: 0, y: currentY), size: headerSize)
            headerAttributes[supplementaryViewIndex] = headerAttribute
            currentY = headerAttribute.frame.maxY + sectionEdgeInsets.top

            //  計算設置每個cell的布局對象
            //  該section一共有多少row
            let rowNum = self.collectionView?.numberOfItems(inSection: i) ?? 0
            var currentX = sectionEdgeInsets.left
            for j in 0..<rowNum {
                let cellIndex = IndexPath(row: j, section: i)
                let cellAttribute = UICollectionViewLayoutAttributes(forCellWith: cellIndex)
                let cellSize = delegate?.collectionView(collectionView, layout: self, sizeForItemAt: cellIndex) ?? CGSize(width: 0, height: 0)
                if currentX + cellSize.width + sectionEdgeInsets.right > collectionView?.frame.width ?? 0 {
                    //  超過collectview換行,并且collectionview的高度增加
                    currentX = sectionEdgeInsets.left
                    currentY = currentY + cellSize.height + minimumLineSpacingForSection
                }
                cellAttribute.frame = CGRect(origin: CGPoint(x: currentX, y: currentY), size: cellSize)
                currentX = currentX + cellSize.width + MaximumSpacing
                cellAttributes[cellIndex] = cellAttribute

                if j == rowNum - 1 {
                    currentY = currentY + cellSize.height + sectionEdgeInsets.bottom
                }
            }

            //  計算每個footer的布局對象
            let footerAttribute = UICollectionViewLayoutAttributes(forSupplementaryViewOfKind: UICollectionElementKindSectionFooter, with: supplementaryViewIndex)
            let footerSize = delegate?.collectionView?(collectionView, layout: self, referenceSizeForFooterInSection: i) ?? CGSize(width: 0, height: 0)
            footerAttribute.frame = CGRect(origin: CGPoint(x: 0, y: currentY), size: footerSize)
            footerAttributes[supplementaryViewIndex] = footerAttribute
            currentY = currentY + footerSize.height
        }

    }

    // MARK:- 當前屏幕可見的cell、header、footer的布局
    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        var attributes = [UICollectionViewLayoutAttributes]()
        //  添加當前屏幕可見的cell的布局
        for element in cellAttributes.values {
            if rect.contains(element.frame) {
                attributes.append(element)
            }
        }
        //  添加當前屏幕可見的頭視圖的布局
        for element in headerAttributes.values {
            if rect.contains(element.frame) {
                attributes.append(element)
            }
        }
        //  添加當前屏幕可見的尾部的布局
        for element in footerAttributes.values {
            if rect.contains(element.frame) {
                attributes.append(element)
            }
        }
        return attributes
    }

    // MARK:- 該方法是為每個Cell返回一個對應的Attributes,我們需要在該Attributes中設置對應的屬性,如Frame等
    override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        return cellAttributes[indexPath]
    }

    // MARK:- 該方法是為每個頭和尾返回一個對應的Attributes,我們需要在該Attributes中設置對應的屬性,如Frame等
    override func layoutAttributesForSupplementaryView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        var attr: UICollectionViewLayoutAttributes?
        if elementKind == UICollectionElementKindSectionHeader {
            attr = headerAttributes[indexPath]
        } else {
            attr = footerAttributes[indexPath]
        }
        return attr
    }

    // MARK:- 設置滾動范圍
    override var collectionViewContentSize: CGSize {
        let width = collectionView?.frame.width ?? 0
        return CGSize(width: width, height: currentY)
    }

}

然后使用幾乎和原先差不多,只是布局的代理方法需要更換成自定義布局的代理方法

//
//  ViewController.swift
//  UICollectionLayout
//
//  Created by David Yu on 2018/4/27.
//  Copyright ? 2018年 David Yu. All rights reserved.
//

import UIKit

class ViewController: UIViewController {

    let titles = ["測試測試測試測試","測試測試測試測試","試測試試測試試測試試測試","試測試試測試試測試試測試試測試試測試","試測試試測試試測試試測試","試測試試測試試測試試測試試測試","試測試試測試試測試試測試試測試試測試","試測試試測試試測試試測試","試測試試測試試測試","試測試試測試試測試試測試試測試","試測試試測試試測試試測試","試測試試測試試測試","試測試","試測試試測試試測試試測試試測試試測試試測試","試測試試測試試測試試測試試測試","試測試試測試試測試"]
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        setView()
    }

    func setView() {
        self.view.backgroundColor = UIColor.white
        let layout = DVMaximumSpacingLayout()
        layout.MaximumSpacing = 12.0
        layout.minimumLineSpacingForSection = 8.0
        layout.sectionEdgeInsets = UIEdgeInsets(top: 12, left: 15, bottom: 12, right: 15)
        layout.delegate = self
        let collection = UICollectionView(frame: CGRect(x: 0, y: 64, width: self.view.frame.width, height: self.view.frame.height-64), collectionViewLayout: layout)
        collection.backgroundColor = UIColor.white
        collection.delegate = self
        collection.dataSource = self
        collection.register(UINib(nibName: "CollectionCell", bundle: Bundle.main), forCellWithReuseIdentifier: "CollectionCell")
        self.view.addSubview(collection)
    }

}

extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource, DVMaximumSpacingLayoutDelegate {

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return titles.count
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionCell", for: indexPath) as! CollectionCell
        cell.title = titles[indexPath.row]
        return cell
    }

    func collectionView(_ collectionView: UICollectionView?, layout collectionViewLayout: DVMaximumSpacingLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let title = titles[indexPath.row]
        let size = title.getSize(UIFont.systemFont(ofSize: 17), size: CGSize(width: UIScreen.main.bounds.width, height: 26))
        return CGSize(width: size.width + 20, height: 26)
    }

}

來看看運行效果圖:

確實是達到了想要的效果,希望可以幫助到有同樣需求的同學。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容