Reputation: 1550
I want to load the cell of a collection view one by one using the API collectionView.insertItems(at:)
. There are many answers online which accomplish this by animating the alpha
of the cell inside of willDisplayCell
and other methods, for example here - link. However, nothing works perfectly. The reason for using collectionView.insertItems(at:)
is that the animation is already built in and no hacks needed.
However using collectionView.insertItems(at:)
, I get error such as:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of items in section 0. The number of items contained in an existing section after the update (5) must be equal to the number of items contained in that section before the update (5), plus or minus the number of items inserted or deleted from that section (1 inserted, 0 deleted) and plus or minus the number of items moved into or out of that section (0 moved in, 0 moved out).
This is an example of what I have, it's crashing as shown above, but it's a start I think in the right direction (hopefully):
class ViewController: UIViewController {
@IBOutlet weak var collectionView: UICollectionView!
/// This array is populated from a network call.
let images = [Image]()
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
animateCells()
}
func animateCells() {
let indexPaths = [IndexPath(item: 0, section: 0),
IndexPath(item: 1, section: 0),
IndexPath(item: 2, section: 0),
IndexPath(item: 3, section: 0),
IndexPath(item: 4, section: 0)]
for indexPath in indexPaths {
let delayTime = 0.1 + Double(indexPath.row) * 0.3
delay(delayTime) {
self.collectionView.performBatchUpdates({
self.collectionView.insertItems(at: [indexPath])
}, completion: nil)
}
}
}
func delay(_ delay: TimeInterval, closure: @escaping () -> Void) {
let when = DispatchTime.now() + delay
DispatchQueue.main.asyncAfter(deadline: when, execute: closure)
}
}
// MARK: - UICollectionViewDataSource
extension ViewController: UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return images.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
return cell
}
}
One of the main problems I see is that I do return images.count
inside of numberOfItemsInSection
.
Any thoughts?
Upvotes: 2
Views: 485
Reputation: 957
I made one demo with your code and found your mistake
When you are inserting items in your collection view there are already 5 items in your collection view in that position so you can not insert new cells there because your images array has only 5 items which are already been displayed.
Please find below working code ->
class ViewController: UIViewController {
@IBOutlet weak var collectionView: UICollectionView!
//Array for collectionView
var images :[UIImage] = []
//This array will populate from network call
var arrAllImagesFromNetwork : [UIImage] = Array(repeating: UIImage(named: "img1")!, count: 5)
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
animateCells()
}
func animateCells() {
let indexPaths = [IndexPath(item: 0, section: 0),
IndexPath(item: 1, section: 0),
IndexPath(item: 2, section: 0),
IndexPath(item: 3, section: 0),
IndexPath(item: 4, section: 0)]
for indexPath in indexPaths {
let delayTime = 0.1 + Double(indexPath.row) * 0.3
delay(delayTime) {
self.images.append(self.arrAllImagesFromNetwork[indexPath.row])
self.collectionView.performBatchUpdates({
self.collectionView.insertItems(at: [indexPath])
}, completion: nil)
}
}
}
func delay(_ delay: TimeInterval, closure: @escaping () -> Void) {
let when = DispatchTime.now() + delay
DispatchQueue.main.asyncAfter(deadline: when, execute: closure)
}
}
// MARK: - UICollectionViewDataSource
extension ViewController: UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return images.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
return cell
}
}
Upvotes: 1
Reputation: 1142
Did you try to append element to images
with delay too?
var images = Array(repeating: 0, count: 0)
delay(delayTime) {
self.collectionView.performBatchUpdates({
self.images.append(0)
self.collectionView.insertItems(at: [indexPath])
}, completion: nil)
}
Upvotes: 1