У меня есть два горизонтальных представления коллекции - один показывает количество сообщений, а другой - временную шкалу, соответствующую датам публикации.Каждый день имеет вертикальную ячейку на временной шкале collectionView.Это окрашено, если есть пост в тот день.
Например, на этом изображении два сообщения в разные дни.Активный пост толще остальных.
Чтобы сделать это более сложным, посты загружаются с помощью NSFetchedResultsController, так что когда пост добавляется или удаляется, postsCollectionView И временная шкала обновляется.Вот метод NSFetchedResults, который вызывается, когда это происходит:
extension TimelineViewController: NSFetchedResultsControllerDelegate {
//autoUpdate Stuff
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>,
didChange anObject: Any,
at indexPath: IndexPath?,
for type: NSFetchedResultsChangeType,
newIndexPath: IndexPath?){
switch type {
case .insert:
print("Insert Object: \(String(describing: newIndexPath))")
blockOperations.append(
BlockOperation(block: { [weak self] in
if let this = self {
this.postsCollectionView!.insertItems(at: [newIndexPath!])
}
})
)
case .update:
blockOperations.append(
BlockOperation(block: { [weak self] in
if let this = self {
this.postsCollectionView!.reloadItems(at: [indexPath!])
}
})
)
case .move:
blockOperations.append(
BlockOperation(block: { [weak self] in
if let this = self {
this.postsCollectionView!.moveItem(at: indexPath!, to: newIndexPath!)
}
})
)
case .delete:
blockOperations.append(
BlockOperation(block: { [weak self] in
if let this = self {
this.postsCollectionView!.deleteItems(at: [indexPath!])
}
})
)
default: break
}
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
postsCollectionView!.performBatchUpdates({ () -> Void in
for operation: BlockOperation in self.blockOperations {
operation.start()
}
}, completion: { (finished) -> Void in
self.loadTimeline()
self.blockOperations.removeAll(keepingCapacity: false)
})
}
}
, а вот функция loadTimeline, которую он вызывает, когда это делается с анимациями
func loadTimeline(){
postTimes = posts.fetchedObjects?.map{ $0.timeStamp } as! [Date]
if postTimes.count > 0 {
detailsView.isHidden = false
timelineCollectionView.isHidden = false
firstPostDate = postTimes.first
lastPostDate = postTimes.last
for (i,postTime) in postTimes.enumerated() {
let day = postTime.days(from: Calendar.current.startOfDay(for: firstPostDate!))
indexMap[day] = IndexPath(row: i, section: 0)
}
let timelineStartDay = Calendar.current.date(byAdding: .day, value: 0, to: firstPostDate!)
timelineStart = Calendar.current.startOfDay(for: timelineStartDay!)
timelineEnd = Calendar.current.date(byAdding: .day, value: 0, to: lastPostDate!)
timelineCollectionView.reloadItems(at: timelineCollectionView.indexPathsForVisibleItems)
let postTimeStamp = currentPost.timeStamp as Date?
let timelineIndex = postTimeStamp?.days(from: timelineStart!)
print(timelineIndex,"timelineIndex")
let cellIndexPath = IndexPath(item: timelineIndex!,section: 0)
self.selectedIndexPath = cellIndexPath
timelineCollectionView.scrollToItem(at: cellIndexPath, at: .centeredHorizontally, animated: true)
} else {
timelineCollectionView.isHidden = true
detailsView.isHidden = true
}
}
Когда я это делаю,Я получаю сообщение об ошибке:
2018-12-10 10:30:10.696195-0800 SweatNetOffline[3922:22319314] *** Assertion failure in -[UICollectionView _endItemAnimationsWithInvalidationContext:tentativelyForReordering:animator:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKitCore_Sim/UIKit-3698.84.15/UICollectionView.m:5956
2018-12-10 10:30:10.705622-0800 SweatNetOffline[3922:22319314] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'attempt to insert item 1 into section 0, but there are only 1 items in section 0 after the update'
Обратите внимание, что эта ошибка вызвана timelineCollectionView.reloadItems(at: timelineCollectionView.indexPathsForVisibleItems)
.
Если я закомментирую это, не произойдет сбой, но, конечно, временная шкала не будет перезагружена,
Также данные для временной шкалы установлены следующим образом:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if collectionView == timelineCollectionView {
let cell = timelineCollectionView.dequeueReusableCell(withReuseIdentifier: "dayCell", for: indexPath) as! TimelineCollectionViewCell
let cellDate = Calendar.current.date(byAdding: .day, value: indexPath.item, to: self.timelineStart!)!
if postTimes.contains(where: { Calendar.current.isDate(cellDate, inSameDayAs: $0) }) {
cell.backgroundColor = UIColor(red:0.22, green:0.45, blue:0.62, alpha:1.0)
}
return cell
} else {
... // for the post cell
}
}
, поскольку postTimes обновляется в loadTimeline (). Я не считаю, что вызов reloadData необходим, я просто хочуперезагрузите элементы, которые видны с обновленными postTimes.Почему здесь не работает вызов reloadItems?