Я структурировал свое приложение, используя шаблон MVVM, datasource
из collectionView
получает данные из viewModel
.
В viewModel
у меня есть замыкание, которое вызывается после обновления данных, оно пропускает IndexSet
разделов и [IndexPath]
элементов, которые collectionView
должны вставить. Однако я получаю сбой каждый раз после вызова метода вставки с ошибкой:
'Неверное обновление: неверное количество разделов. Количество разделов, содержащихся в представлении коллекции после обновления (11), должно быть равно количеству разделов, содержащихся в представлении коллекции до обновления (11), плюс или минус количество вставленных или удаленных разделов (11 вставлено, 0 Исключено) ".
Я понимаю, что означает эта ошибка, однако я заметил, что это порядок, в котором методы называются
1. viewDidLoad()
2. numberOfSections(in collectionView: UICollectionView) // returns 0
3. update?(insertedSectionsSet, insertedRows) // the closure gets called in viewModel, insertedSectionsSet has 11 elements, that's correct
4. numberOfSections(in collectionView: UICollectionView) // returns 11, that's correct but it should be called after next point
5. viewModel.update = { [weak self] sections, items in
DispatchQueue.main.async {
self?.collectionView.performBatchUpdates({
self?.collectionView.insertSections(sections) // crash
self?.collectionView.insertItems(at: items)
}, completion: nil)
}
}
Как вы можете видеть из приведенного ниже кода, я добавил распечатки перед вызовом каждого метода, и вы можете четко видеть, что numberOfSections вызывается после вызова замыкания и перед его выполнением. Это не имеет никакого смысла для меня, почему это происходит. Я полагаю, что причина сбоя заключается в вызове numberOfSections перед их вставкой, поскольку после вставки ожидается 22 секции.
обновление
NumberOfSections
закрытие
NumberOfSections
Код:
class PlaylistsDataSource: NSObject, UICollectionViewDataSource {
...
func numberOfSections(in collectionView: UICollectionView) -> Int {
print("numberofsections")
return viewModel.numberOfSections
}
...
}
class PlaylistsMasterViewController: UIViewController {
...
viewDidLoad() {
viewModel.update = { [weak self] sections, items in
DispatchQueue.main.async {
print("closure")
self?.collectionView.performBatchUpdates({
self?.collectionView.insertSections(sections)
self?.collectionView.insertItems(at: items)
}, completion: nil)
}
}
}
...
}
class PlaylistsMasterViewModel {
private var sectionIndexes = [String]()
private var playlistsDictionary = [String: [AWPlaylist]]()
var update: ((IndexSet, [IndexPath]) -> Void)?
var numberOfSections: Int {
return sectionIndexes.count
}
РЕДАКТИРОВАТЬ: добавлено больше кода
extension PlaylistsMasterViewModel {
func fetchPlaylists() {
repo.getAllPlaylists(from: [.iTunes]) { [weak self] result in
switch result {
case .success(let playlists):
self?.sortIncoming(playlists: playlists)
case .failure(let error):
print(error.localizedDescription)
}
}
}
private func sortIncoming(playlists: [AWPlaylist]) {
var insertedPlaylists = [(key: String, list: AWPlaylist)]()
var insertedIndexes = [String]()
func insertNewSection(playlist: AWPlaylist, key: String) {
insertedIndexes.append(key)
playlistsDictionary.updateValue([playlist], forKey: key)
}
func insertNewRow(playlist: AWPlaylist, key: String) {
guard var value = playlistsDictionary[key] else {
print("Oh shit")
return
}
value.append(playlist)
value.sort(by: SortingPredicates.Playlist.nameAscending)
playlistsDictionary.updateValue(value, forKey: key)
insertedPlaylists.append((key, playlist))
}
for list in playlists {
let name = list.localizedName.uppercased().trimmingCharacters(in: CharacterSet.whitespaces)
guard let firstCharacter = name.first else { return }
let firstLetter = String(firstCharacter)
let key: String
if CharacterSet.english.contains(firstLetter.unicodeScalars.first!) {
key = firstLetter
} else if CharacterSet.numbers.contains(firstLetter.unicodeScalars.first!) {
key = "#"
} else {
key = "?"
}
if playlistsDictionary[key] == nil {
insertNewSection(playlist: list, key: key)
} else {
insertNewRow(playlist: list, key: key)
}
}
sectionIndexes.append(contentsOf: insertedIndexes)
sectionIndexes.sort(by: { $0 < $1 })
let insertedSections = insertedIndexes.compactMap { index -> Int? in
guard let sectionIndex = self.sectionIndexes.firstIndex(of: index) else {
return nil
}
return sectionIndex
}
let insertedSectionsSet = IndexSet(insertedSections)
let insertedRows = insertedPlaylists.compactMap { tuple -> IndexPath? in
if let section = self.sectionIndexes.firstIndex(of: tuple.key) {
if let row = self.playlistsDictionary[tuple.key]?.firstIndex(where: { $0.equals(tuple.list) }) {
return IndexPath(row: row, section: section)
}
}
return nil
}
print("update")
update?(insertedSectionsSet, insertedRows)
}