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

UICollectionView Выполнение обновлений с использованием executeBatchUpdates

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

Как только у меня появятся мои данные, я хотел бы сделать следующее:

[self.collectionView performBatchUpdates:^{
    for (UIImage *image in images) {
        [self.collectionView insertItemsAtIndexPaths:****]
    }
} completion:nil];

Теперь вместо *** я должен передать массив NSIndexPaths, который должен указывать на местоположение новых элементов, которые нужно вставить. Я очень смущен, так как после предоставления местоположения, как мне представить фактическое изображение, которое должно отображаться в этой позиции?

Спасибо


UPDATE:

resultsSize содержит размер массива источников данных self.results, прежде чем новые данные будут добавлены из данных в newImages.

[self.collectionView performBatchUpdates:^{

    int resultsSize = [self.results count];
    [self.results addObjectsFromArray:newImages];
    NSMutableArray *arrayWithIndexPaths = [NSMutableArray array];

    for (int i = resultsSize; i < resultsSize + newImages.count; i++)
          [arrayWithIndexPaths addObject:[NSIndexPath indexPathForRow:i inSection:0]];

          [self.collectionView insertItemsAtIndexPaths:arrayWithIndexPaths];

} completion:nil];
4b9b3361

Ответ 1

Смотрите Вставка, удаление и перемещение разделов и элементов из Руководство по программированию коллекции для iOS ":

Чтобы вставить, удалить или переместить один раздел или элемент, вы должны следовать эти шаги:

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

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

Итак, в вашем случае вы должны сначала добавить изображение в источник данных просмотра коллекции, а затем вызвать insertItemsAtIndexPaths. Затем представление коллекции попросит функцию делегата источника данных предоставить представление для вставленного элемента.

Ответ 2

Я только что реализовал это с помощью Swift. Поэтому я хотел бы поделиться своей реализацией. Сначала инициализируйте массив NSBlockOperations:

    var blockOperations: [NSBlockOperation] = []

В контроллере изменится, перезапустите массив:

func controllerWillChangeContent(controller: NSFetchedResultsController) {
    blockOperations.removeAll(keepCapacity: false)
}

В методе изменения объекта:

    func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {

    if type == NSFetchedResultsChangeType.Insert {
        println("Insert Object: \(newIndexPath)")

        blockOperations.append(
            NSBlockOperation(block: { [weak self] in
                if let this = self {
                    this.collectionView!.insertItemsAtIndexPaths([newIndexPath!])
                }
            })
        )
    }
    else if type == NSFetchedResultsChangeType.Update {
        println("Update Object: \(indexPath)")
        blockOperations.append(
            NSBlockOperation(block: { [weak self] in
                if let this = self {
                    this.collectionView!.reloadItemsAtIndexPaths([indexPath!])
                }
            })
        )
    }
    else if type == NSFetchedResultsChangeType.Move {
        println("Move Object: \(indexPath)")

        blockOperations.append(
            NSBlockOperation(block: { [weak self] in
                if let this = self {
                    this.collectionView!.moveItemAtIndexPath(indexPath!, toIndexPath: newIndexPath!)
                }
            })
        )
    }
    else if type == NSFetchedResultsChangeType.Delete {
        println("Delete Object: \(indexPath)")

        blockOperations.append(
            NSBlockOperation(block: { [weak self] in
                if let this = self {
                    this.collectionView!.deleteItemsAtIndexPaths([indexPath!])
                }
            })
        )
    }
}

В используемом методе раздела разделов:

func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) {

    if type == NSFetchedResultsChangeType.Insert {
        println("Insert Section: \(sectionIndex)")

        blockOperations.append(
            NSBlockOperation(block: { [weak self] in
                if let this = self {
                    this.collectionView!.insertSections(NSIndexSet(index: sectionIndex))
                }
            })
        )
    }
    else if type == NSFetchedResultsChangeType.Update {
        println("Update Section: \(sectionIndex)")
        blockOperations.append(
            NSBlockOperation(block: { [weak self] in
                if let this = self {
                    this.collectionView!.reloadSections(NSIndexSet(index: sectionIndex))
                }
            })
        )
    }
    else if type == NSFetchedResultsChangeType.Delete {
        println("Delete Section: \(sectionIndex)")

        blockOperations.append(
            NSBlockOperation(block: { [weak self] in
                if let this = self {
                    this.collectionView!.deleteSections(NSIndexSet(index: sectionIndex))
                }
            })
        )
    }
}

И, наконец, в контроллере did изменился метод контента:

func controllerDidChangeContent(controller: NSFetchedResultsController) {        
    collectionView!.performBatchUpdates({ () -> Void in
        for operation: NSBlockOperation in self.blockOperations {
            operation.start()
        }
    }, completion: { (finished) -> Void in
        self.blockOperations.removeAll(keepCapacity: false)
    })
}

Я лично добавил код в методе deinit, чтобы отменить операции, когда ViewController собирается освободиться:

deinit {
    // Cancel all block operations when VC deallocates
    for operation: NSBlockOperation in blockOperations {
        operation.cancel()
    }

    blockOperations.removeAll(keepCapacity: false)
}

Ответ 3

Я столкнулся с подобной проблемой при удалении элемента из индекса, и это то, что я думаю, что нам нужно делать, используя метод performBatchUpdates:.

1 # сначала вызовите deleteItemAtIndexPath, чтобы удалить элемент из представления коллекции.

2 # Удалить элемент из массива.

3 # Просмотр списка обновления, перезагружая данные.

[self.collectionView performBatchUpdates:^{
            NSIndexPath *indexPath = [NSIndexPath indexPathForRow:sender.tag inSection:0];
            [self.collectionView deleteItemsAtIndexPaths:[NSArray arrayWithObject:indexPath]];
            [self.addNewDocumentArray removeObjectAtIndex:sender.tag];
        } completion:^(BOOL finished) {
            [self.collectionView reloadData];
        }];

Это поможет мне удалить все сбои при сбое и утверждении.

Ответ 4

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

Пакетные обновления Preform не знают, что вы хотели добавить раздел X + 1, когда вставляете элемент в X + 1, X. без добавления этого раздела в.

Убедитесь, что вы используете раздел вставки и удаления: https://developer.apple.com/documentation/uikit/uicollectionview/1618090-insertsections https://developer.apple.com/documentation/uikit/uicollectionview/1618102-deletesections