警告:UICollectionViewFlowLayout 的缓存帧与索引路径不匹配 'abc'

Warning: UICollectionViewFlowLayout has cached frame mismatch for index path 'abc'

这是导致警告的代码:

private override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes? {
    let attributes = super.layoutAttributesForItemAtIndexPath(indexPath)
    let distance = CGRectGetMidX(attributes!.frame) - self.midX;
    var transform = CATransform3DIdentity;
    transform = CATransform3DTranslate(transform, -distance, 0, -self.width);
    attributes!.transform3D = CATransform3DIdentity;
    return attributes
}

控制台还打印:

This is likely occurring because the flow layout "xyz" is modifying attributes returned by UICollectionViewFlowLayout without copying them.

如何修复此警告?

This is likely occurring because the flow layout "xyz" is modifying attributes returned by UICollectionViewFlowLayout without copying them

果然,这正是您正在做的事情:

private override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes? {
    let attributes = super.layoutAttributesForItemAtIndexPath(indexPath)
    let distance = CGRectGetMidX(attributes!.frame) - self.midX;
    var transform = CATransform3DIdentity;
    transform = CATransform3DTranslate(transform, -distance, 0, -self.width);
    attributes!.transform3D = CATransform3DIdentity;
    return attributes
}

我希望你只要说:

let attributes = 
    super.layoutAttributesForItemAtIndexPath(indexPath).copy() 
    as! UICollectionViewLayoutAttributes

或类似的,问题就会消失。

除了上面的好答案。

我知道示例代码是用 swift 编写的,但我认为 Objective-C 版本会有所帮助。

对于Objective-C,这是行不通的,因为复制函数只进行浅拷贝。您必须这样做:

NSArray * original   = [super layoutAttributesForElementsInRect:rect];
NSArray * attributes = [[NSArray alloc] initWithArray:original copyItems:YES];

为了便于阅读,我添加了一个临时变量。

添加到@Georgi 回答

<NSCopying> 必须符合并添加复制消息调用到 layoutAttributesForItemAtIndexPath

UICollectionViewLayoutAttributes* attributes = [[super layoutAttributesForItemAtIndexPath:indexPath] copy];

我在覆盖 layoutAttributesForElementsInRect 时遇到了这个问题。遍历 super.layoutAttributesForElementsInRect(rect) 数组中的每个元素并调用复制对我不起作用,所以我最终回到了基础 类 并使用 NSArraycopyItems

override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
    // unwrap super's attributes
    guard let superArray = super.layoutAttributesForElementsInRect(rect) else { return nil }

    // copy items
    guard let attributes = NSArray(array: superArray, copyItems: true) as? [UICollectionViewLayoutAttributes] else { return nil }

    // modify attributes

    return attributes
}

我已将 UICollectionViewFlowLayout 子类化。在 layoutAttributesForElementsInRect() 里面我做了这个改变:

变化自

guard let attributesForItem: UICollectionViewLayoutAttributes = self.layoutAttributesForItemAtIndexPath(indexPath) else {
    return
}

改为

guard let attributesForItem = self.layoutAttributesForItemAtIndexPath(indexPath)?.copy() as? UICollectionViewLayoutAttributes else {
    return
}

这不是原始问题的答案,但可以帮助 layoutAttributesForElements(in rect: CGRect) (Swift 3.0):

let safeAttributes = super.layoutAttributesForElements(in: rect)?.map { [=10=].copy() as! UICollectionViewLayoutAttributes }
safeAttributes?.forEach { /* do something with attributes*/ }

已更新 Swift 3!

的回复

对于 func layoutAttributesForElements

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

    guard let attributes = super.layoutAttributesForElements(in: rect) else {
        return nil
    }

    guard let attributesToReturn =  attributes.map( { [=10=].copy() }) as? [UICollectionViewLayoutAttributes] else {
        return nil
    }

    return attributesToReturn

}

对于 func layoutAttributesForItem

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

    guard let currentItemAttributes = super.layoutAttributesForItem(at: indexPath)?.copy() as? UICollectionViewLayoutAttributes else {
        return nil
    }

    return currentItemAttributes
}

如果重写这两个函数,则必须在这两个函数上调用复制!

写得不错!