I have used a collection view to display a collection of images. It is working, but when I make use of the function didDeselectItemAt
it will crash if I click certain images.
Error that occurs:
fatal error: unexpectedly found nil while unwrapping an Optional value
numberOfItemsInSection:
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return images.count
}
cellForItemAt:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cellIdentifier = "ImageCell"
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellIdentifier, for: indexPath) as! ImageCollectionViewCell
let imageUrls = collection?.imageUrls
// Configure the cell
// Reset alpha of first item in collection
if indexPath.row == 0 {
cell.imageView.alpha = 1.0
if videoUrl != nil {
cell.backgroundColor = UIColor.red
cell.imageView.alpha = 0.5
}
}
cell.imageView.af_setImage(withURL: URL(string: (imageUrls?[indexPath.row])!)!)
return cell
}
didDeselectItemAt:
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath) as! ImageCollectionViewCell
UIView.animate(withDuration: 0.3, animations: {
cell.imageView.alpha = 0.6
})
}
didSelectItemAt:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("You've tapped me. NICE!! \(indexPath.row)")
let cell = collectionView.cellForItem(at: indexPath) as! ImageCollectionViewCell
cell.imageView.alpha = 1
let url = self.collection?.imageUrls?[indexPath.row]
self.selectedImageUrl = url
Alamofire.request(url!).responseImage(completionHandler: {
(response) in
if response.result.value != nil {
self.selectedImage.image = response.result.value
}
})
}
Above code is working, but will throw an error if I click certain images. The first cell of every collection view has a alpha of 100% - 1.0 and all other ones has an alpha of 0.6. What I noticed is that - except the first cell of the collection view - every 7th cell has also an alpha of 100% and if the collection view contains a videoUrl it will have a red background and an alpha of 50%..
Is this because of the reusable cells like the UITableViewController, where you use dequeueReusableCell
and should you use that function too inside the didDeselectItemAt
or is something else not correctly implemented?
So only the first cell in a collection view should have an alpha of 100, all other 60%. If the object has a videoUrl, the first element will have an alpha of 50% with a red background.
If there are any questions left, please let me know. Thanks in advance and have a great evening!
UPDATE:
The following print line will output below code when I click the first image of the collection view and after that the second one. This doesn't crash the app and it will still work
print("OUTPUT \(String(describing: collectionView.cellForItem(at: indexPath)))")
Above code snippet I've placed in both didSelectItemAt
and didDeselectItemAt
:
didSelectItemAt output:
OUTPUT Optional(<CollectionViewApp.ImageCollectionViewCell: 0x7ff61679c1a0; baseClass = UICollectionViewCell; frame = (150 0; 150 100); clipsToBounds = YES; opaque = NO; layer = <CALayer: 0x6080000347c0>>)
didDeselectItemAt output:
OUTPUT Optional(>)
When I click the first or second image and then the last image in the collection view, the error will occur and will crash my app. The touch on the first image will give me the output:
OUTPUT Optional(<CollectionViewApp.ImageCollectionViewCell: 0x7fc3cb038540; baseClass = UICollectionViewCell; frame = (150 0; 150 100); clipsToBounds = YES; opaque = NO; layer = <CALayer: 0x610000225980>>)
The last item - that will let my app crash - will throw the following output:
OUTPUT Optional(<CollectionViewApp.ImageCollectionViewCell: 0x7fc3cb03afb0; baseClass = UICollectionViewCell; frame = (300 0; 150 100); clipsToBounds = YES; opaque = NO; layer = <CALayer: 0x6100002264c0>>)
.
So the crash can be triggered, when you click one of the first images in the collection view and after that one of the last images in the collection view, when the first images are out of canvas. I used a horizontal collection view.
So the crash is happening because when selecting an item, didDeselectItemAt
can be called for an item that's not visible anymore.
The UICollectionView
reuses UICollectionViewCells
, this means that the collectionView.cellForItem(at: indexPath)
method will return nil for cells that are not visible.
To fix this you can use the following code:
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
guard let cell = collectionView.cellForItem(at: indexPath) as! ImageCollectionViewCell else {
return //the cell is not visible
}
UIView.animate(withDuration: 0.3, animations: {
cell.imageView.alpha = 0.6
})
}
Some other suggestions I have for improving your code:
You have a lot of places where you're force unwrapping values.
Consider taking the following approach:
guard let url = self.collection?.imageUrls?[indexPath.row] else {
fatalError("url was nil")
}
self.selectedImageUrl = url
Alamofire.request(url).responseImage(completionHandler: {
(response) in
if response.result.value != nil {
self.selectedImage.image = response.result.value
}
})
By using the guard statement you force the app to crash with an appropriate error message, which will help you when debugging. It's a better practice compared to force unwrapping.
Also, when dequeing cells, you could go for something like this:
let cellIdentifier = "ImageCell"
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellIdentifier, for: indexPath) as? ImageCollectionViewCell else {
fatalError("Wrong cell type")
}
This can happen when the cell type is different
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With