7

Я застрял в странной катастрофе и пытался исправить ее весь день. У меня есть пользовательский UICollectionViewLayout, который в основном добавляет силы тяжести и столкновения по отношению к ячейкам.CollectionView + UIKit Динамика сбой на performBatchUpdates:

Реализация отлично работает! Проблема возникает, когда я пытаюсь удалить одну ячейку, используя: [self.collectionView performBatchUpdates:].

Это дает мне следующую ошибку:

2013-12-12 21:15:35.269 APPNAME[97890:70b] *** Assertion failure in -[UICollectionViewData validateLayoutInRect:], /SourceCache/UIKit_Sim/UIKit-2935.58/UICollectionViewData.m:357 

2013-12-12 20:55:49.739 APPNAME[97438:70b] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UICollectionView recieved layout attributes for a cell with an index path that does not exist: <NSIndexPath: 0x975d290> {length = 2, path = 0 - 4}' 

Моя модель обрабатывается правильно, и я могу видеть, что удаление элемента из него!

Индексированные объекты элемента для удаления передаются правильно между объектами. Единственный раз, когда обновление коллекцииView не возникает, когда я удаляю последнюю ячейку на нем, в противном случае происходит сбой.

Вот код, который я использую для удаления ячейки.

- (void)removeItemAtIndexPath:(NSIndexPath *)itemToRemove completion:(void (^)(void))completion 
{ 
    UICollectionViewLayoutAttributes *attributes = [self.dynamicAnimator layoutAttributesForCellAtIndexPath:itemToRemove]; 

    [self.gravityBehaviour removeItem:attributes]; 
    [self.itemBehaviour removeItem:attributes]; 
    [self.collisionBehaviour removeItem:attributes]; 

    [self.collectionView performBatchUpdates:^{ 
     [self.fetchedBeacons removeObjectAtIndex:itemToRemove.row]; 
     [self.collectionView deleteItemsAtIndexPaths:@[itemToRemove]]; 
    } completion:nil]; 
} 

Делегаты CollectionView, которые обрабатывают ячейки, являются базовыми ниже.

- (CGSize)collectionViewContentSize 
{ 
    return self.collectionView.bounds.size; 
} 

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect 
{ 
    return [self.dynamicAnimator itemsInRect:rect]; 
} 

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath 
{ 
    return [self.dynamicAnimator layoutAttributesForCellAtIndexPath:indexPath]; 
} 

Вещи, которые я уже пытался, но безуспешно: - основание недействительности макет - обновление данных - Удаление поведения из UIDynamicAnimator и добавить их снова после обновления

Любые идеи?

Исходный код с проблемой доступен в этом репозитории. Пожалуйста, проверьте это. Code Repository

Лучший. Джордж.

+0

Попробуйте добавить некоторый код в метод делегата данных, который возвращает ячейку и мониторинга запросов на консоли. Аналогичным образом добавьте регистрацию в методы, возвращающие число секций, количество ячеек. Возможно, cv запрашивает последнее, прежде чем он даже начнет пакетное обновление (в этом случае вам нужно переместить маяк, чтобы удалить из блока раньше. На самом деле, я сочувствую - пакетное обновление работало отлично для меня, я сделал небольшой измените мои ячейки (добавленные виды) и wham, он начал рушиться повсюду, и мне пришлось сделать все изменения, используя reload :-( –

+0

Спасибо Дэвиду. Вот вывод последовательности загрузки, удаления и сбоя. ok. [Ссылка на журнал вывода] (https://dl.dropboxusercontent.com/u/7373916/output.txt) –

+0

Можете ли вы добавить журналы до и после executeBatchUpdates и внутри блока до, в середине и после каждой строки кода Также, чтобы сделать меня счастливым, добавьте в журнал количество элементов в self.fetchedBeacons после удаления (сделайте это сообщение в среднем журнале), затем обновите журнал. Я знаю, что это PITA, но то, что я делал с CV, написано очень просто одно приложение просмотра, которое использовало метки в ячейках, чтобы играть с различными режимами работы вещи. Если это работает, и сложный код не сводит его CV (скорее всего). Кажется, CV меняет поведение на то, насколько сложны клетки, что действительно затрудняет нам! –

ответ

11

Через несколько недель борются с этой ошибкой, некоторые полезные идеи от наших друзей @ Давида-х и @erwin и некоторые звонки и электронные письма от ДМВР Apple, я был в состоянии понять это выпуск вне. Просто хочу поделиться решением с сообществом.

  1. Проблема. У UIKit Dynamics есть некоторые вспомогательные методы для работы с Collection Views, и эти методы фактически не делают некоторые внутренние вещи, которые, я думаю, должны делать автоматически, например, автоматически обновлять динамически анимированные ячейки, когда выполняется какое-то действие с использованием performBatchUpdates:. Таким образом, вы должны сделать это вручную в соответствии с WWDR, ​​поэтому мы перебираем обновляемые обновления, обновляя их NSIndexPaths, чтобы динамический аниматор мог дать нам правильные обновления в ячейках.

  2. Ошибка. Даже делая это обновление IndexPath при вставке/удалении ячеек, у меня было много случайных сбоев и странного поведения с анимацией. Итак, я следил за советом, данным @erwin, который состоит в повторном создании UIDynamicAnimator после performBatchUpdates:, и это устраняет все проблемы с подобной ситуацией.

Итак, код.

- (void)removeItemAtIndexPath:(NSIndexPath *)itemToRemove completion:(void (^)(void))completion 
{ 
    UICollectionViewLayoutAttributes *attributes = [self.dynamicAnimator layoutAttributesForCellAtIndexPath:itemToRemove]; 

    if (attributes) { 
     [self.collisionBehaviour removeItem:attributes]; 
     [self.gravityBehaviour removeItem:attributes]; 
     [self.itemBehaviour removeItem:attributes]; 

     // Fix the problem explained on 1. 
     // Update all the indexPaths of the remaining cells 
     NSArray *remainingAttributes = self.collisionBehaviour.items; 
     for (UICollectionViewLayoutAttributes *attributes in remainingAttributes) { 
      if (attributes.indexPath.row > itemToRemove.row) 
       attributes.indexPath = [NSIndexPath indexPathForRow:(attributes.indexPath.row - 1) inSection:attributes.indexPath.section]; 
     } 

     [self.collectionView performBatchUpdates:^{ 
      completion(); 
      [self.collectionView deleteItemsAtIndexPaths:@[itemToRemove]]; 
     } completion:nil]; 

     // Fix the bug explained on 2. 
     // Re-instantiate the Dynamic Animator 
     self.dynamicAnimator = [[UIDynamicAnimator alloc] initWithCollectionViewLayout:self]; 
     [self.dynamicAnimator addBehavior:self.collisionBehaviour]; 
     [self.dynamicAnimator addBehavior:self.gravityBehaviour]; 
     [self.dynamicAnimator addBehavior:self.itemBehaviour]; 
    } 
} 

Я открыл радиолокатор, объяснив проблему, ожидая, что Apple исправит это при дальнейшем обновлении. Если кто-то хочет его дублировать, он доступен по адресу OpenRadar.

Спасибо всем.

+0

Вы тестировали, если это все еще проблема в iOS 7.1? –

2

Во-первых, удивительный проект! Мне нравится, как ящики подпрыгивают. Кажется, что всегда есть один недостающий - ряд 0. В любом случае, любой, кто читает это, кто заинтересован в макете, должен его увидеть! Любите анимацию.

В ... Layout.m:

Изменено этот метод просто перезагрузить:

- (void)removeItemAtIndexPath:(NSIndexPath *)itemToRemove completion:(void (^)(void))completion 
{ 
    //assert([NSThread isMainThread]); 

    UICollectionViewLayoutAttributes *attributes = [self.dynamicAnimator layoutAttributesForCellAtIndexPath:itemToRemove]; 
    [self.collisionBehaviour removeItem:attributes]; 
    [self.gravityBehaviour removeItem:attributes]; 
    [self.itemBehaviour removeItem:attributes]; 

    completion(); 

    [self.collectionView reloadData]; 
} 

Я добавил эти сообщения журнала:

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect 
{ 
    NSLog(@"ASKED FOR LAYOUT ELEMENTS IN RECT"); 
    NSArray *foo = [self.dynamicAnimator itemsInRect:rect]; 
    NSLog(@"...LAYOUT ELEMENTS %@", foo); 
    return foo; 
} 

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

CollectionViewDynamics[95862:70b] ASKED FOR LAYOUT ELEMENTS IN RECT 
CollectionViewDynamics[95862:70b] ...LAYOUT ELEMENTS (
    "<UICollectionViewLayoutAttributes: 0x109405530> index path: (<NSIndexPath: 0xc000000000008016> {length = 2, path = 0 - 1}); frame = (105.41 -102.09; 100.18 100.18); transform = [0.99999838000043739, -0.0017999990280001574, 0.0017999990280001574, 0.99999838000043739, 0, 0]; ", 
    "<UICollectionViewLayoutAttributes: 0x10939c2b0> index path: (<NSIndexPath: 0xc000000000018016> {length = 2, path = 0 - 3}); frame = (1 -100.5; 100 100); ", 
    "<UICollectionViewLayoutAttributes: 0x10912b200> index path: (<NSIndexPath: 0xc000000000000016> {length = 2, path = 0 - 0}); frame = (3 468; 100 100); " 
) 
CollectionViewDynamics[95862:70b] *** Assertion failure in -[UICollectionViewData validateLayoutInRect:], /SourceCache/UIKit_Sim/UIKit-2935.80.1/UICollectionViewData.m:357 

Фиксация, которая должна вас заставить (я надеюсь). NSLog был моим лучшим другом годами! YMMV

+0

Привет @ david-h! Большое спасибо. Рад, что вам понравилась анимация. Окончательный взгляд еще более интересен. Позвольте мне оставаться на связи, чтобы я мог показать вам, когда он готов. Но успеха здесь нет. Обновление индексных путей - это то, что нужно перезагрузить CV, и это вручную вводит множество других странных ошибок. Так как я немного устал заполнять Radars/Bugreports и никогда не получаю обратную связь, я использовал один из запросов поддержки моей учетной записи. Посмотрим, сможет ли Apple Engineer понять это. Я буду более рад опубликовать окончательный ответ (если есть). Спасибо и позаботьтесь! –

3

Я боролся с подобной ситуацией.

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

Для меня удаляется средний UICollectionViewCell, который работает (без сбоев), но приложение затем сработает, если я попытаюсь удалить последнюю ячейку.

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

Проблема представляет собой несоответствие между количеством ячеек в представлении коллекции и количеством элементов, возвращаемых динамическим аниматором -itemsInRect: (все мои ячейки всегда видны).

Я могу предотвратить сбой, удалив все действия до того, как я удалю ячейку. Но, конечно, это приводит к нежелательному движению моих клеток, когда вложения уходят.

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

Итак, наконец, я придумал механизм, основанный на сохранении поведения в стороне, переустановке динамического аниматора и повторном добавлении поведения.

Кажется, что он хорошо работает и, возможно, может быть дополнительно оптимизирован.

- (void)detachItemAtIndexPath:(NSIndexPath *)indexPath completion:(void (^)(void))completion { 

for (UIAttachmentBehavior *behavior in dynamicAnimator.behaviors) { 
    UICollectionViewLayoutAttributes *thisItem = [[behavior items] firstObject]; 
    if (thisItem.indexPath.row == indexPath.row) { 
     [dynamicAnimator removeBehavior:behavior]; 
    } 
    if (thisItem.indexPath.row > indexPath.row) { 
     thisItem.indexPath = [NSIndexPath indexPathForRow:thisItem.indexPath.row-1 inSection:0]; 
    } 
} 

NSArray *tmp = [NSArray arrayWithArray:dynamicAnimator.behaviors]; 

self.dynamicAnimator = [[UIDynamicAnimator alloc] initWithCollectionViewLayout:self]; 

for (UIAttachmentBehavior *behavior in tmp) { 
    [dynamicAnimator addBehavior:behavior]; 
} 

    // custom algorithm to place cells 
for (UIAttachmentBehavior *behavior in dynamicAnimator.behaviors) { 
    [self setAnchorPoint:behavior]; 
} 

[self.collectionView performBatchUpdates:^{ 
    [self.collectionView deleteItemsAtIndexPaths:@[indexPath]]; 
} completion:^(BOOL finished) { 
      completion(); 
}]; 

}

0

решить подобную проблему одной струне

self.<#custom_layout_class_name#>.dynamicAnimator = nil; 

Придется бросить его каждый раз обновляя DATASOURCE

+0

Это была одна из моих первых попыток решить проблему, и она породила еще один крах, связанный с представлением коллекции. –

 Смежные вопросы

  • Нет связанных вопросов^_^