Populating UICollectionView with Images from Camera Roll

Hi,
I'm trying to recreate an image gallery like the one on the "Photos" App of the iPhone.

This is the code I'm using (Swift 5):
Code Block
import UIKit
import Photos
private let reuseIdentifier = "dataCell"
class GalleryCollectionViewController: UICollectionViewController {
    var images = [PHAsset]()
    override func viewDidLoad() {
        super.viewDidLoad()
        self.collectionView!.register(GalleryImageCell.self, forCellWithReuseIdentifier: reuseIdentifier)
        getImages()
    }
    
    func getImages() {
        let assets = PHAsset.fetchAssets(with: PHAssetMediaType.image, options: nil)
        assets.enumerateObjects({ (object, count, stop) in
            self.images.append(object)
        })
        
        self.images.reverse()
        self.collectionView!.reloadData()
    }
    
    override func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }
    
    
    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return images.count
    }
    
    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "dataCell", for: indexPath) as! GalleryImageCell
        
        let asset = images[indexPath.row]
        let ImageManager = PHImageManager.default()
        if cell.tag != 0 {
            ImageManager.cancelImageRequest(PHImageRequestID(cell.tag))
        }
        
        cell.tag = Int(ImageManager.requestImage(for: asset, targetSize: CGSize(width: 120, height: 120), contentMode: .aspectFill, options: nil, resultHandler: { (result, _) in
            cell.galleryImage?.image = result
        }))
        
        return cell
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let width = self.view.frame.width * 0.32
        let height = self.view.frame.height * 0.179910045
        return CGSize(width: width, height: height)
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return 2.5
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
        return UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
        return 0
    }
    
}

Anyway, when I load the view I only get a black view. The images are not displayed at all even though the cells seem to be created as the view scrolls for a good amount of time.

It is not a problem related to Privacy Permissions as I've already granted access to "All Photos" and because ''images'' array contains all the photos assets.

Do you know what I might be doing wrong?

Thank you in advance,
Alberto

Several questions here:

Code Block
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "dataCell", for: indexPath) as! GalleryImageCell
let asset = images[indexPath.row]
let ImageManager = PHImageManager.default()
if cell.tag != 0 {
ImageManager.cancelImageRequest(PHImageRequestID(cell.tag))
}
cell.tag = Int(ImageManager.requestImage(for: asset, targetSize: CGSize(width: 120, height: 120), contentMode: .aspectFill, options: nil, resultHandler: { (result, _) in
cell.galleryImage?.image = result
}))
return cell
}
  • line 6, are you sure the tag is 0 ?

should add a print to check:
Code Block
if cell.tag != 0 {
ImageManager.cancelImageRequest(PHImageRequestID(cell.tag))
print("Exists, tag is", cell.tag)
}
  • result is optional.

Did you try to unwrap result
Code Block
cell.tag = Int(ImageManager.requestImage(for: asset, targetSize: CGSize(width: 120, height: 120), contentMode: .aspectFill, options: nil, resultHandler: { (result, _) in
cell.galleryImage?.image = result ?? UIImage()
}))

  • Are you sure to get an image ?

To check, try to copy the image in a single UIImageView, to check you get some image.

Note:
var names should start with lowercase: imageManager vs ImageManager


Can you tell what happens if you remove this line (line 14)?
Code Block
self.collectionView!.register(GalleryImageCell.self, forCellWithReuseIdentifier: reuseIdentifier)


If you designed your GalleryImageCell as the custom cell on the storyboard, you should not call register({class}, forCellWithReuseIdentifier:). All the settings made on the storyboard will not be used when you call register in this style.
(Please do not forget to set the reuse Identifier dataCell to the Identifier of the cell in the Attribute Inspector.)


Your code may cause some other problems, but that is another issue.

If I remove line 14 as you suggested I get this error message:
Code Block 2021-03-16 13:53:51.666072+0100 Flowshot[6490:1274036] [core] "Error returned from daemon: Error Domain=com.apple.accounts Code=7 "(null)""
2021-03-16 13:53:51.771564+0100 Flowshot[6490:1274036] [Storyboard] Unknown class GalleryImageCell in Interface Builder file.
2021-03-16 13:53:51.772326+0100 Flowshot[6490:1274036] * Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<UICollectionViewCell 0x102ec0680> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key galleryImage.'
* First throw call stack:
(0x1a36bd9d8 0x1b7a43b54 0x1a35ce6e4 0x1a487aca8 0x1a64317b8 0x1a5b6f254 0x1a35a8298 0x1a5b6b218 0x1a56bb3e4 0x1ec094180 0x1a56bba44 0x1024825f8 0x1024831f4 0x1a56a8984 0x1a56ad514 0x1a56b1ac0 0x1a6432398 0x1a6937df4 0x1a693e398 0x1a641ecb0 0x1a576cb68 0x1a5768f68 0x1a576e9a8 0x1a6425704 0x1a576e8dc 0x1a5893410 0x1a588ff00 0x1a64254d4 0x1a63fa634 0x1a63f9524 0x1a63f6ce4 0x1a6426e80 0x1a6427308 0x1a63f27dc 0x1a6427804 0x1a63f2624 0x1a63f1f18 0x1a588bf78 0x1a589400c 0x1a59c208c 0x1a5893f8c 0x1a64253d8 0x1a5893df4 0x1a575a35c 0x1a63d1f08 0x1a5f37984 0x1a5f26eb4 0x1a5f58484 0x1a363c87c 0x1a3636f50 0x1a3637498 0x1a3636ba0 0x1ba39f598 0x1a5f282f4 0x1a5f2d874 0x1b6db7b54 0x102474644 0x1024745bc 0x102474688 0x1a3315568)
libc++abi.dylib: terminating with uncaught exception of type NSException
*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<UICollectionViewCell 0x102ec0680> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key galleryImage.'
terminating with uncaught exception of type NSException


I also tried to check if I do really get an Image and yes I'm able to fill a single UIImageView. The problem is that the cells remain black.
Accepted Answer

If I remove line 14 as you suggested I get this error message:

2021-03-16 13:53:51.771564+0100 Flowshot[6490:1274036] [Storyboard] Unknown class GalleryImageCell in Interface Builder file.

Thanks for trying. Seems you have not set Custom Class of the cell correctly.
Can you see the grayed module name of your project in the Module field?
If not (then, you may see grayed None), check Inherit Module From Target.
(In some cases, you need to uncheck and re-check.)

Of course, you may need to reconfirm the content in the Class field, no extra whitespace before or after the class name?

Please try until you do not see Unknown class GalleryImageCell in Interface Builder file.
There may be other things to fix, but resolving this Unknown class would be the first thing to do.
Thank you so much. Inherit Module From Target did the job. Now it is working as planned.
Populating UICollectionView with Images from Camera Roll
 
 
Q