当单元格具有动态宽度时,CollectionView 中的间距不正确?

Posted

技术标签:

【中文标题】当单元格具有动态宽度时,CollectionView 中的间距不正确?【英文标题】:Spacing is not correct in CollectionView when cell having dynamic width? 【发布时间】:2018-05-25 12:09:22 【问题描述】:

我想实现一个TokenView(芯片视图)风格的视图。为此,我在 UITableViewCell 内使用集合视图,CollectionView 有自定义单元格。在自定义单元格内有UILabel。现在单元格的宽度取决于UILabel 的内容。

下面是自定义 UITableViewCell Cell 的代码

import UIKit

class LanguageTableViewCell: UITableViewCell,UICollectionViewDataSource,UICollectionViewDelegate,UICollectionViewDelegateFlowLayout

        var arr:[String] = ["English","Intermediate","English","Intermediate","English","Intermediate","English","Intermediate","English","Intermediate","English","Intermediate","English","Intermediate","English","Intermediate","English","Intermediate","English","Intermediate","English","Intermediate"]
    @IBOutlet weak var collectionView: UICollectionView!

    override func awakeFromNib() 
        super.awakeFromNib()
        collectionView.dataSource = self
        collectionView.delegate = self
        let layout = UICollectionViewFlowLayout()
        layout.minimumInteritemSpacing = 0
        layout.minimumLineSpacing = 0
        layout.scrollDirection = .vertical
        self.collectionView.collectionViewLayout = layout
        // Initialization code
    


    override func setSelected(_ selected: Bool, animated: Bool) 
        super.setSelected(selected, animated: animated)

        // Configure the view for the selected state
    

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

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
        let cell = self.collectionView.dequeueReusableCell(withReuseIdentifier: "LanguageCollectionViewCell", for: indexPath) as! LanguageCollectionViewCell
        cell.setValue(lang: arr[indexPath.row],indexPath:indexPath)
        return cell
    



    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize 
        let string = arr[indexPath.row]
        let width = (string.count * 10)
        return CGSize(width:width, height: 30)
    


UICollectionViewCell 的类

typealias Parameters = [String:Any]
import UIKit


class LanguageCollectionViewCell: UICollectionViewCell 

    @IBOutlet weak var languageLabel: UILabel!

    override func layoutSubviews() 
        super.layoutSubviews()

    

    func setValue(lang:String ,indexPath:IndexPath) 

        if indexPath.row % 2 == 0 
            languageLabel.backgroundColor = UIColor.gray

        else 
            languageLabel.backgroundColor = UIColor.yellow

        
            self.languageLabel.text = lang
    

【问题讨论】:

见raywenderlich.com/164608/… github.com/keighl/KTCenterFlowLayout 你在用stackView吗? 【参考方案1】:

在您的 UITableViewCell Cell 中实现这一点

    子类化UICollectionViewFlowLayout 添加这样的实现:

解决方案是减少单元格间距/对齐 UICollectionView 单元格布局。非常简单和快速的性能。基于Olloqui给出的例子

类 CustomViewFlowLayout

class CustomViewFlowLayout: UICollectionViewFlowLayout 

let cellSpacing:CGFloat = 4

    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? 
            self.minimumLineSpacing = 4.0
            self.sectionInset = UIEdgeInsets(top: 10, left: 8, bottom: 10, right: 6)
            let attributes = super.layoutAttributesForElements(in: rect)

            var leftMargin = sectionInset.left
            var maxY: CGFloat = -1.0
            attributes?.forEach  layoutAttribute in
                if layoutAttribute.frame.origin.y >= maxY 
                    leftMargin = sectionInset.left
                
                layoutAttribute.frame.origin.x = leftMargin
                leftMargin += layoutAttribute.frame.width + cellSpacing
                maxY = max(layoutAttribute.frame.maxY , maxY)
            
            return attributes
    

其中 cellSpacing 可以设置为您喜欢的任何值。这个技巧保证单元格之间的空间将完全等于 cellSpacing。

UITableViewCell Cell/CollectionViewController 的实现。

import UIKit

class ViewController: UIViewController , UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout

@IBOutlet weak var collectionView: UICollectionView!

var arr:[String] = ["English","Intermediate","English","English","arr","UICollectionViewFlowLayoutFlowFlowFlow","English","UICollectionViewDelegate","English","Intermediate","UIViewController","viewDidLoad","Intermediate","String","Intermediate","arr","Intermediate","UIKit","Intermediate","English","columnLayout","English","languageLabel"]

let columnLayout = CustomViewFlowLayout()

override func viewDidLoad() 
    super.viewDidLoad()
    collectionView.collectionViewLayout = columnLayout
    collectionView.contentInsetAdjustmentBehavior = .always



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


func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "chip", for: indexPath) as! LanguageCollectionViewCell
    cell.languageLabel.text = arr[indexPath.row]
    return cell


func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize 

    let string = arr[indexPath.row]

    // your label font.
    let font = UIFont.systemFont(ofSize: 16)
    let fontAttribute = [NSAttributedStringKey.font: font]

    // to get the exact width for label according to ur label font and Text.
    let size = string.size(withAttributes: fontAttribute)

    // some extraSpace give if like so.
    let extraSpace : CGFloat = 8.0
    let width = size.width + extraSpace
    return CGSize(width:width, height: 30)
   

UICollectionViewCell 的类

class LanguageCollectionViewCell: UICollectionViewCell 
    @IBOutlet weak var languageLabel: UILabel!


【讨论】:

【参考方案2】:

我也用过一个吊舱

pod 'AlignedCollectionViewFlowLayout'

在我使用UICollectionView 的课堂上,

import AlignedCollectionViewFlowLayout

并配置collectionViewLayout如下

if let fl = myCollection.collectionViewLayout as? AlignedCollectionViewFlowLayout           
  fl.estimatedItemSize = UICollectionViewFlowLayoutAutomaticSize
  fl.horizontalAlignment = .left
  fl.verticalAlignment = .top

请注意,您可以使用此设置左/右和上/下对齐 荚!它对我来说就像一个魅力

我还确保将preferredMaxLayoutWidth 添加到UICollectionViewCell 的标签中,以便修复可能出现的错误之一(即单元格的宽度不能大于屏幕尺寸)

class MyCollectionViewCell: UICollectionViewCell 
    override func awakeFromNib() 
        super.awakeFromNib()
        // Initialization code
        myLabel.preferredMaxLayoutWidth = UIScreen.main.bounds.size.width * 0.8
    

我的收藏结果如下:

【讨论】:

【参考方案3】:

1-实现UICollectionViewDelegateFlowLayout协议,

添加以下代码:

var itemsPerRow:CGFloat = 3

让 sectionInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)

然后添加委托方法来确定间距,如下所示;

func collectionView(_ collectionView: UICollectionView,
                        layout collectionViewLayout: UICollectionViewLayout,
                        sizeForItemAt indexPath: IndexPath) -> CGSize 
        let paddingSpace = sectionInsets.left * (itemsPerRow + 1)
        let availableWidth = view.frame.width - paddingSpace

        let widthPerItem = availableWidth / itemsPerRow

        return CGSize(width: widthPerItem, height: widthPerItem)
    

    func collectionView(_ collectionView: UICollectionView,
                        layout collectionViewLayout: UICollectionViewLayout,
                        insetForSectionAt section: Int) -> UIEdgeInsets 
        return sectionInsets
    

    func collectionView(_ collectionView: UICollectionView,
                        layout collectionViewLayout: UICollectionViewLayout,
                        minimumLineSpacingForSectionAt section: Int) -> CGFloat 
        return 10
    

【讨论】:

【参考方案4】:

你可以试试

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize 

    let lb = UILabel()

    lb.text = arr[indexPath.row]

    lb.font = UIFont.init(name: "System", size: 17)

    lb.sizeToFit()

    return CGSize(width:lb.frame.size.width, height: 30 )

//

或者设置这个

layout.estimatedItemSize = UICollectionViewFlowLayoutAutomaticSize

并为单元格提供基于 0 的前导、尾随、顶部和底部约束

注意:在这两种情况下,您都必须删除方法sizeForItemAt的实现

【讨论】:

以上是关于当单元格具有动态宽度时,CollectionView 中的间距不正确?的主要内容,如果未能解决你的问题,请参考以下文章

集合视图中单元格的宽度不正确

如何使用自动布局动态调整具有固定宽度的collectionview单元格高度?

如何限制标题单元格的宽度

UICollectionViewCell 宽度的动态变化

具有可变宽度单元格的 UITableView

uicollectionview 动态高度但固定宽度