Предупреждение: UICollectionViewFlowLayout имеет кэшированное несоответствие кадра для пути указателя '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
}

Консоль также печатает:

Это, вероятно, происходит потому, что макет потока "xyz" изменяет атрибуты, возвращаемые UICollectionViewFlowLayout, не копируя их.

Как исправить это предупреждение?

Ответы

Ответ 1

Вероятно, это происходит потому, что макет потока "xyz" изменяет атрибуты, возвращаемые UICollectionViewFlowLayout, не копируя их

И, конечно же, это то, что вы делаете:

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

или аналогичный, проблема исчезнет.

Ответ 2

В дополнение к большому ответу выше.

Я знаю, что код примера написан быстрым, но я подумал, что может быть полезно иметь версию Objective-C.

Для Objective-C это не будет работать, потому что функция копирования выполняет только мелкую копию. Вам нужно будет сделать это:

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

Я добавил временную переменную для удобочитаемости.

Ответ 3

У меня была эта проблема при переопределении layoutAttributesForElementsInRect. Итерация через каждый элемент массива super.layoutAttributesForElementsInRect(rect) и вызов копия не работали для меня, поэтому я в итоге вернулся к классам Foundation и использовал NSArray copyItems:

override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
    // unwrap super 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
}

Ответ 4

Добавление к ответу @Georgi

<NSCopying> должен быть согласован и добавить вызов сообщения на layoutAttributesForItemAtIndexPath

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

Ответ 5

Это не ответ на оригинальный вопрос, но может помочь для layoutAttributesForElements (в rect: CGRect) (Swift 3.0):

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

Ответ 6

Обновлен ответ для 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( { $0.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
}

Если вы переопределите обе функции, вам нужно вызвать копию на обе функции!

Хорошее кодирование!

Ответ 7

Я подклассифицировал UICollectionViewFlowLayout. Внутри layoutAttributesForElementsInRect() я сделал это изменение:

изменить

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

измените на

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