Предупреждение: 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, не копируя их.

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

7 ответов

Решение

Это, вероятно, происходит, потому что макет потока "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

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

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

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

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

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

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

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

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
}

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

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

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

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

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

Обновленный ответ для Swift 3!

для макета funcAttributesForElements

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
}

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

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

Я подкласс UICollectionViewFlowLayout, внутри layoutAttributesForElementsInRect() Я сделал это изменение:

меняться от

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

изменить на

guard let attributesForItem = self.layoutAttributesForItemAtIndexPath(indexPath)?.copy() as? UICollectionViewLayoutAttributes else {
    return
}
Другие вопросы по тегам