1

I have implemented a viewController which contains a collectionView that holds photos from the users photo album saved on their phone. On the very first launch of the app after the app asks for the users permission to access their photos the photos don't show in the collectionView. But after quitting the app and relaunching the photos display as they should.

Below is the code of the viewController in question

import UIKit
import Photos

class ViewController: UIViewController,UICollectionViewDelegate,UICollectionViewDelegateFlowLayout,UICollectionViewDataSource {

    var imageArray:[UIImage] = [UIImage]()

    var collectionView:UICollectionView! = {
        let layout = UICollectionViewFlowLayout()
        layout.minimumInteritemSpacing = 1
        layout.minimumLineSpacing = 1
        layout.scrollDirection = .vertical
        let CV = UICollectionView(frame: UIScreen.main.bounds, collectionViewLayout: layout)
        CV.translatesAutoresizingMaskIntoConstraints = false
        return CV
    }()

    var fetchResult: PHFetchResult<PHAsset> = PHFetchResult()

    override func viewDidLoad() {
        super.viewDidLoad()
        self.collectionView.delegate = self
        self.collectionView.dataSource = self
        collectionView.register(customPhotoCell.self, forCellWithReuseIdentifier: "Cell")
        self.view.addSubview(collectionView)
        setUpLayout()
        fetchAssets()
    }

    func setUpLayout(){
        collectionView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 50).isActive = true
        collectionView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 0).isActive = true
        collectionView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: 0).isActive = true
        collectionView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 0).isActive = true
    }

    func fetchAssets(){
        let fetchOptions = PHFetchOptions()
        fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
        fetchResult = PHAsset.fetchAssets(with: .image, options: fetchOptions)
    }

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return fetchResult.count
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! customPhotoCell
        let imageView = cell.imgView
        let requestOptions = PHImageRequestOptions()
        requestOptions.isSynchronous = true
        requestOptions.deliveryMode = .highQualityFormat
        PHImageManager.default().requestImage(for: fetchResult.object(at: indexPath.item) , targetSize: CGSize(width: 200,height: 200), contentMode: .aspectFill, options: requestOptions, resultHandler: {
            image,error in
            if let error = error{
                print(error)
            }
            self.imageArray.append(image!)
            imageView.image = image!
        })
        return cell
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return 1
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
        return 1
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let width = self.view.bounds.width/3 - 1
        return CGSize(width: width, height: width)
    }

}

class customPhotoCell:UICollectionViewCell{

    var imgView:UIImageView = {
        let imgV = UIImageView()
        imgV.backgroundColor = .orange
        imgV.translatesAutoresizingMaskIntoConstraints = false
        return imgV
    }()

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.addSubview(imgView)
        imgView.topAnchor.constraint(equalTo: self.topAnchor, constant: 0).isActive = true
        imgView.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 0).isActive = true
        imgView.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 0).isActive = true
        imgView.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: 0).isActive = true
    }
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

}

extension UIImageView{
    func fetchImage(asset: PHAsset, contentMode: PHImageContentMode, targetSize: CGSize) {
        let options = PHImageRequestOptions()
        options.version = .original
        PHImageManager.default().requestImage(for: asset, targetSize: targetSize, contentMode: contentMode, options: options) { image, _ in
            guard let image = image else { return }
            switch contentMode {
            case .aspectFill:
                self.contentMode = .scaleAspectFill
            case .aspectFit:
                self.contentMode = .scaleAspectFit
            }
            self.image = image
        }
    }
}

extension UIImage{
    func resizeImageWith() -> UIImage {
        let aspectRatio = CGFloat(self.size.width / self.size.height)
        let newWidth = UIScreen.main.bounds.width
        let newSize = CGSize(width: newWidth, height: newWidth/aspectRatio)
        UIGraphicsBeginImageContextWithOptions(newSize, true, 0)
        self.draw(in: CGRect(origin: CGPoint(x: 0, y: 0), size: newSize))
        let  newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return newImage!
    }
}
  • I think you need to re-fetch the photos after the users gives access to his library. On relaunch, the fetch works immediately because the user ALREADY gave access to his pictures. – Mikael Apr 11 '18 at 01:51
  • If you can make a new screen just to ask access to the photos and explain why you need it. and when you get access, you dismiss the view and show your collection. OR ask for access and when you get the completion block with access allowed, re-do a fetch (be sure you call it on the main thread for safety reasons) – Mikael Apr 11 '18 at 01:52
  • let try reload data when app did become active – Scott.N Apr 11 '18 at 04:44

2 Answers2

1

First check if you have permission or not. If you are authorized only then initialize fetchResult and reload collectionView.

func checkPhotoLibraryPermission() {
    let status = PHPhotoLibrary.authorizationStatus()
    switch status {
    case .authorized:
        self.fetchAssets()
        self.cv.reloadData()
    case .denied, .restricted : break
    //handle denied status
    case .notDetermined:
        // ask for permissions
        PHPhotoLibrary.requestAuthorization() { status in
            switch status {
            case .authorized:
                DispatchQueue.main.async {
                    self.fetchAssets()
                    self.cv.reloadData()
                }
            // as above
            case .denied, .restricted: break
            // as above
            case .notDetermined: break
                // won't happen but still
            }
        }
    }

Call this method in viewDidLoad() instead of fetchAssets(). Hope this helps.

Chanchal Chauhan
  • 1,530
  • 13
  • 25
0

Reload your collectionView in viewDidAppear,after the app asks for the users permission,viewDidAppear method will call.

colllectionView.reloadData()
Naren Krish
  • 259
  • 2
  • 14