Подтвердить что ты не робот

Сдвиг анимации потока при вставке нескольких пользовательских дополнительных представлений

При вставке нескольких пользовательских дополнительных представлений в виде коллекции с существующими дополнительными представлениями, используя подкласс UICollectionViewFlowLayout, появится представление коллекции для создания сглаженных анимаций или иначе неправильно обрабатывать вставки.

Вставка одного дополнительного представления ведет себя так, как ожидалось, но вставка двух дополнительных представлений сразу приводит к очевидному визуальному сбою, и вставка множества дополнительных представлений сразу (например, 4+) ухудшает сбой. (iOS 10.2, iOS 9.3, Swift 3, Xcode 8.2.1)

Здесь демонстрация:

Анимация, показывающая два элемента и два дополнительных вида, вставляемых в представление коллекции с простой компоновкой потока.

Вы можете воспроизвести проблему, используя этот примерный проект. В этом упрощенном примере для каждого элемента есть одно дополнительное представление, а два элемента (с двумя дополнительными представлениями) вставляются во время каждого обновления пакета. В моем реальном проекте у меня меньше дополнительных представлений, чем элементов, и я использую другие возможности компоновки потока.

Мое лучшее предположение заключается в том, что что-то вызывает представление коллекции, путающее пути указателя или атрибуты макета, соответствующие существующим/вставленным дополнительным представлениям. Если это окажется ошибкой в ​​одном из классов Apple, я был бы очень благодарен за ваш самый элегантный способ обхода.

Попытки решения

Я пробовал и дважды проверял каждое из следующих:

  • Внедрение indexPathsToInsertForSupplementaryView(ofKind:) - насколько я могу судить, это просто, и мой реализация возвращает правильные пути указателей. Представления всегда вставлены. Для дополнительных представлений заголовка и нижнего колонтитула UICollectionViewFlowLayout, похоже, возвращает отсортированный массив указательных путей, но сортировка массива для меня не меняла.

  • Предоставление исходных и окончательных атрибутов макета -calling super для этих методов, похоже, возвращает правильные фреймы, поэтому неясно, какие корректировки (если они есть) могут быть сделаны в первоначальные или окончательные атрибуты макета для решения проблемы. Но см. Третье любопытство, указанное ниже.

  • Недействительность макета. Кажется, не имеет никакого значения, будет ли макет вручную аннулирован полностью или частично до, во время или после performBatchUpdates() или вообще.

  • Пользовательские пути указателей. Невозможно, чтобы пути индекса для дополнительных элементов не соответствовали элементам указателей элемента, насколько я могу судить, и, несмотря на документацию об обратном, UICollectionViewFlowLayout выйдет из строя (путем запроса атрибутов компоновки для несуществующих путей указателей), если дополнительные элементы такого же типа не будут последовательно пронумерованы из 0. Я предполагаю, что представление коллекции и/или объект макета могут вычислять новые пути индекса при вставке элементов или удаляется (т.е. путем увеличения или уменьшения свойства item указательного пути). Поэтому создание произвольных путей индекса не является вариантом.

При отладке заметили курьезы

  • Визуальные сбои специфичны для дополнительных представлений и не встречаются для вставленных элементов, даже если указательные пути и фреймы одинаковы для обоих.

  • Отладка иерархии представлений показывает, что, хотя фреймы вставленных дополнительных представлений верны, фреймы некоторых существующих дополнительных представлений неверны. Это так, несмотря на то, что кадры, возвращаемые объектом макета для существующих представлений на этих указательных путях, выглядят правильно.

  • При вставке нескольких дополнительных представлений вызовы, сделанные представлением коллекции для первоначальных и окончательных атрибутов макета, выглядят неуравновешенными. То есть атрибуты первоначальной компоновки запрашиваются несколько раз для одного и того же пути указателя (и, естественно, одни и те же атрибуты возвращаются каждый раз), а запросов конечного макета меньше. Кроме того, для некоторых существующих дополнительных представлений первоначальные и окончательные атрибуты макета никогда не запрашиваются вообще. Я нахожу это подозрительным. Это заставляет меня думать, что представление коллекции каким-то образом путает дополнительные представления и/или пути индекса до и после обновления.

  • Адреса памяти для экземпляров IndexPath, созданных моим кодом Swift, поразительно отличаются от экземпляров NSIndexPath, созданных внутри UICollectionView и UICollectionViewFlowLayout. Первые всегда разные (как и ожидалось), в то время как последние кажутся одинаковыми между запусками (например, [0, 0], [0, 1] и [0, 2] всегда находятся в 0xc000000000000016, 0xc000000000200016 и 0xc000000000400016). В Swift IndexPath подключается к NSIndexPath, но первый тип значения, тогда как последний является ссылочным типом. NSIndexPath также использует тегированные указатели. Существует отдаленная возможность того, что эти два были несовершенно мосты и/или что классы представления коллекции полагаются внутренне на поведение NSIndexPath каким-то образом, что приводит к путанице в указателе, очевидной здесь. Я не знаю, как проверить это или принять это дальше.

Похожие вопросы

Следующие вопросы могут быть связаны, хотя ни один из ответов не работал у меня:

Проблема также может быть связана с этот отчет об ошибках в Open Radar. Примерный проект, сопровождающий этот отчет, слишком сложный, чтобы принести много пользы.

Техническая поддержка Apple

После публикации этого вопроса я отправил Apple сообщение о технической поддержке. Поддержка Apple Developer ответила следующим образом:

Наши инженеры рассмотрели ваш запрос и определили, что это лучше всего будет обрабатывать как отчет об ошибке.

Пожалуйста, отправьте полный отчет об ошибке, касающийся этой проблемы, используя инструмент "Отчет об ошибках" в https://developer.apple.com/bug-reporting/.

...

Мы провели некоторое время, исследуя возможность обходного пути и, к сожалению, пришли с пустыми руками. Пожалуйста, следуйте за своей ошибкой. Если мы найдем возможность какого-то обходного пути в будущем, мы продолжим. Извините, у меня нет новостей.

Если у вас возникла эта проблема, дублируйте rdar://30510010.

4b9b3361

Ответ 1

Ниже представлен результат CustomCollectionViewController файла indexPath item Row для cellForItemAt indexPath и Row Additional для viewForSupplementaryElementOfKind.

введите описание изображения здесь

Как вы видите, cellForItemAt правильно возвращает indexpath.item но в viewForSupplementaryElementOfKind не возвращается ожидаемый ответ. Я не понял причину до сих пор, если я скоро узнаю о вас.

Итак, для повторного решения вы можете иметь дело с массивом, добавляя в него новые данные и перезагружая collectionView вместо indexPath.

Ответ 2

Я не уверен, есть ли у меня правильное решение для начальной проблемы, но у меня была такая же проблема, как в ответе от @agent_stack.

Если у вас есть собственный макет представления коллекций (подкласс UICollectionViewLayout или подкласс UICollectionViewFlowLayout), и вы добавляете свое собственное дополнительное представление, вам необходимо переопределить дополнительный метод: indexPathsToInsertForSupplementaryView(ofKind elementKind: String)

Вид коллекции вызывает этот метод всякий раз, когда вы добавляете ячейки или разделы в представление коллекции. Реализация этого метода дает вашему объекту макета возможность добавлять новые дополнительные представления в дополнение к дополнениям.

Я сделал свой собственный CollectionViewFlowLayout и только после добавления этих методов в совокупности все анимация, как и должно быть!

    override func prepare(forCollectionViewUpdates updateItems: [UICollectionViewUpdateItem]) {
    super.prepare(forCollectionViewUpdates: updateItems)

    // Prepare update by storing index path to insert
    // ────────────────────────────────────────────────────────────

    // IMPORTANT: Item vs. Section Update
    //
    // An instance of UICollectionViewUpdateItem can represent either an item or a section update,
    // and the only way to tell which is that update items which represent section updates contain
    // index paths with a valid section index and NSNotFound for the item index, while update items
    // that represent item updates contain index paths with valid item indexes.
    //
    // This appears to be completely undocumented by Apple, but it is absolutely consistent behavior.

    // https://www.raizlabs.com/blog/2013/10/animating_items_uicollectionview/

    var indexPaths = [IndexPath]()

    for updateItem in updateItems {

        switch updateItem.updateAction {

        case .insert:

            // If it is a section update then convert NSNotFound to zero.
            // In our case a section update always has only the first item.

            if var indexPath = updateItem.indexPathAfterUpdate {
                if indexPath.item == NSNotFound { indexPath.item = 0 }
                indexPaths.append(indexPath)
            }

        default:
            break
        }
    }

    indexPathsToInsert = indexPaths
}

override func indexPathsToInsertForSupplementaryView(ofKind elementKind: String) -> [IndexPath] {

    // Extra add supplementary index paths
    // ────────────────────────────────────────────────────────────

    // The collection view calls this method whenever you add cells or sections to the collection view.
    // Implementing this method gives your layout object an opportunity to add new supplementary views
    // to complement the additions.

    // http://stackoverflow.com/a/38289843/7441991

    switch elementKind {

    case UICollectionElementKindDateSeparator:
        return indexPathsToInsert

    case UICollectionElementKindAvatar:
        return indexPathsToInsert

    default:
        // If it is not a custom supplementary view, return attributes from flow layout
        return super.indexPathsToInsertForSupplementaryView(ofKind: elementKind)
    }
}

Не стесняйтесь обращаться ко мне, если у вас есть вопросы!:)