UICollectionViewLayout 未按预期工作

Posted

技术标签:

【中文标题】UICollectionViewLayout 未按预期工作【英文标题】:UICollectionViewLayout not working as expected 【发布时间】:2016-10-18 09:16:33 【问题描述】:

我使用 xcode 8 和 swift 3 从 raywenderlich.com 关注了这个 Custom Collection View Layout tutorial。

在实现教程中要求的所有方法后运行应用程序时,出现以下错误:

'-layoutAttributesForItemAtIndexPath 没有 UICollectionViewLayoutAttributes 实例:length = 2, path = 0 - 11'

所以我在我的 CollectionViewFlowLayout 类中添加了以下方法:

override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? 

    return self.cache[indexPath.row]

这几乎可以正常工作,只是向下滚动时某些单元格会覆盖现有单元格然后消失。如果我向上滚动,一切正常。

我还不了解这段代码的完整逻辑,但我已经检查和测试了几次,我不明白代码的哪一部分触发了这种行为。

有什么想法吗?

import UIKit

/* The heights are declared as constants outside of the class so they can be easily referenced elsewhere */
struct UltravisualLayoutConstants 
struct Cell 
    /* The height of the non-featured cell */
    static let standardHeight: CGFloat = 100
    /* The height of the first visible cell */
    static let featuredHeight: CGFloat = 280


class UltravisualLayout: UICollectionViewLayout 
// MARK: Properties and Variables

/* The amount the user needs to scroll before the featured cell changes */
let dragOffset: CGFloat = 180.0

var cache = [UICollectionViewLayoutAttributes]()

/* Returns the item index of the currently featured cell */
var featuredItemIndex: Int 
    get 
        /* Use max to make sure the featureItemIndex is never < 0 */
        return max(0, Int(collectionView!.contentOffset.y / dragOffset))
    


/* Returns a value between 0 and 1 that represents how close the next cell is to becoming the featured cell */
var nextItemPercentageOffset: CGFloat 
    get 
        return (collectionView!.contentOffset.y / dragOffset) - CGFloat(featuredItemIndex)
    


/* Returns the width of the collection view */
var width: CGFloat 
    get 
        return collectionView!.bounds.width
    


/* Returns the height of the collection view */
var height: CGFloat 
    get 
        return collectionView!.bounds.height
    


/* Returns the number of items in the collection view */
var numberOfItems: Int 
    get 
        return collectionView!.numberOfItems(inSection: 0)
    


// MARK: UICollectionViewLayout

/* Return the size of all the content in the collection view */
override var collectionViewContentSize : CGSize 

    let contentHeight = (CGFloat(numberOfItems) * dragOffset) + (height - dragOffset)

    return CGSize(width: width, height: contentHeight)


override func prepare() 


    let standardHeight = UltravisualLayoutConstants.Cell.standardHeight

    let featuredHeight = UltravisualLayoutConstants.Cell.featuredHeight

    var frame = CGRect.zero

    var y:CGFloat = 0

    for item in 0 ... (numberOfItems - 1) 

        let indexPath = NSIndexPath(item: item, section: 0)

        let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath as IndexPath)

        attributes.zIndex = item

        var height = standardHeight

        if indexPath.item == featuredItemIndex 

            let yOffset = standardHeight * nextItemPercentageOffset

            y = self.collectionView!.contentOffset.y - yOffset

            height = featuredHeight


         else if item == (featuredItemIndex + 1) && item != numberOfItems 

            let maxY = y + standardHeight

            height = standardHeight + max((featuredHeight - standardHeight) * nextItemPercentageOffset, 0)

            y = maxY - height
        

        frame = CGRect(x: 0, y: y, width: width, height: height)

        attributes.frame = frame

        cache.append(attributes)

        y = frame.maxY
    


/* Return all attributes in the cache whose frame intersects with the rect passed to the method */

override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? 

    var layoutAttributes = [UICollectionViewLayoutAttributes]()

    for attributes in cache 

        if attributes.frame.intersects(rect) 

            layoutAttributes.append(attributes)
        
    

    return layoutAttributes


override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? 

    return self.cache[indexPath.row]


/* Return true so that the layout is continuously invalidated as the user scrolls */
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool 

    return true


【问题讨论】:

【参考方案1】:

刚刚在拥有 UICollection 视图的视图控制器的 super.viewDidLoad 中添加了它,它现在按预期工作。

这是一个快速修复,我相信有更合适的方法来解决这个问题,更好地管理来自 ios10 的预取功能。

if #available(iOS 10.0, *) 
    collectionView!.prefetchingEnabled = false

【讨论】:

以上是关于UICollectionViewLayout 未按预期工作的主要内容,如果未能解决你的问题,请参考以下文章

UICollectionViewLayout 示例

UICollectionViewLayout 教程

我应该继承啥 UICollectionViewLayout 或 UICollectionViewFlowLayout

使用缓存的 UICollectionViewLayoutAttributes 子类化 UICollectionViewLayout

UICollectionViewLayout - 2 列中的 3 个单元格

为啥将 UICollectionViewLayout 分配给 UICollectionView 后 UICollectionViewCells 不再可见?