cellForItemAt 在 Swift collectionView 中只调用一次

Posted

技术标签:

【中文标题】cellForItemAt 在 Swift collectionView 中只调用一次【英文标题】:cellForItemAt called only once in Swift collectionView 【发布时间】:2019-03-18 14:10:58 【问题描述】:

如果我将流布局与 collectionView 一起使用,那么我的所有单元格对数据都是可见的。如果我使用自定义布局,则 cellForItemAt 仅针对索引 (0,0) 访问,并且相应地仅显示单个单元格。

我很困惑为什么 - 请帮忙!

下面的小例子:

视图控制器:

import UIKit
private let reuseIdentifier = "customCell"

class customCollectionViewController: UICollectionViewController 

@IBOutlet var customCollectionView: UICollectionView!


let dwarfArray = ["dopey", "sneezy", "bashful", "grumpy", "doc", "happy", "sleepy"]


override func viewDidLoad() 
    super.viewDidLoad()


override func numberOfSections(in collectionView: UICollectionView) -> Int 
    return 1



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


override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! customCollectionViewCell

    let cellContentsIndex = indexPath.row
    if cellContentsIndex <= dwarfArray.count
    
        cell.displayContent(name: dwarfArray[cellContentsIndex])
    

    return cell

自定义单元格

import UIKit
class customCollectionViewCell: UICollectionViewCell 
@IBOutlet weak var nameLabel: UILabel!

required init?(coder aDecoder: NSCoder) 
        super.init(coder: aDecoder)
        setup()
    

    override init(frame: CGRect)
        super.init(frame: frame)
    

    public func displayContent(name: String)
       nameLabel.text = name

    

func setup()
    self.layer.borderWidth = 1.0
    self.layer.borderColor = UIColor.black.cgColor

自定义布局 如果这不在这里 - 我可以看到我期望的所有单元格(尽管没有我喜欢的布局)。当我使用它时,我只看到一个单元格。

import UIKit
class customCollectionViewLayout: UICollectionViewLayout 
let CELL_SIZE = 100.0

    var cellAttrsDictionary = Dictionary<NSIndexPath, UICollectionViewLayoutAttributes>()
    //define the size of the area the user can move around in within the collection view
    var contentSize = CGSize.zero

    var dataSourceDidUpdate = true

    func collectionViewContentSize() -> CGSize
        return self.contentSize
    

    override func prepare() 
        if (collectionView?.numberOfItems(inSection: 0))! > 0 
            /// cycle through each item of the section
            for item in 0...(collectionView?.numberOfItems(inSection: 0))!-1
                /// build the collection attributes
                let cellIndex = NSIndexPath(item: item, section: 0)
                let xPos = Double(item)*CELL_SIZE
                let yPos = 40.0

                let cellAttributes = UICollectionViewLayoutAttributes(forCellWith: cellIndex as IndexPath)
                cellAttributes.frame = CGRect(x: xPos, y:yPos, width: CELL_SIZE, height: CELL_SIZE)
                // cellAttributes.frame = CGRect(x: xPos, y:yPos, width: CELL_WIDTH + 2*CELL_SPACING, height: CELL_HEIGHT)
                cellAttributes.zIndex = 1

                //save
                cellAttrsDictionary[cellIndex] = cellAttributes
            
        
    

    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? 
        /// create array to hold all the elements in our current view
        var attributesInRTect = [UICollectionViewLayoutAttributes]()

        /// check each element to see if they should be returned
        for cellAttributes in cellAttrsDictionary.values  
            if rect.intersects(cellAttributes.frame)
            
                attributesInRTect.append(cellAttributes)
            
        

        return attributesInRTect
    

    override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? 
        return cellAttrsDictionary[indexPath as NSIndexPath]!
    

    override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool 
        return true
    

输出

【问题讨论】:

【参考方案1】:

问题在于 contentSize 值

 func collectionViewContentSize() -> CGSize
        return self.contentSize
 

只需将func collectionViewContentSize()... 替换为以下内容:

    func lastLayoutAttributes() -> UICollectionViewLayoutAttributes? 
        return cellAttrsDictionary.values.map  $0 .sorted(by:  $0.frame.maxX < $1.frame.maxX ).last        
    

    override var collectionViewContentSize: CGSize 
        guard let collectionView = collectionView else  return .zero 
        guard collectionView.frame != .zero else  return .zero 

        let width: CGFloat
        let height: CGFloat = collectionView.frame.height

        if let lastLayoutAttributes = lastLayoutAttributes() 
            width = lastLayoutAttributes.frame.maxX
         else 
            width = 0
        

        return CGSize(width: width, height: height)
    

你会看到不止一个单元格。

【讨论】:

以上是关于cellForItemAt 在 Swift collectionView 中只调用一次的主要内容,如果未能解决你的问题,请参考以下文章

CollectionView 方法 cellForItemAt indexPath: 未被调用 (Swift)

Xcode Swift UICollectionView:cellForItemAt IndexPath 未调用但 numberOfItemsInSection 调用

collectionView cellForItemAt 没有被调用

UICollectionViewController项插入动画Swift

UITableViewCell indexPath错误中的Swift UICollectionView

reloadData 没有调用 cellForItemAt