iosswiftvideophfetchoptions

Videos and its thumbnails not saved in sequence after fetching From Photos


I am using below code for getting videos(URL, Durations, Thumbnails). After Fetching data i am displaying it on CollectionView. The main problem is that, some video and its thumbnails not match each other. Can any one please tell me how i can fix it, or any other better solution? Thanks

  override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! CollectionViewCell

        cell.imgV.image = photoLibrary[indexPath.row]

        let duration: TimeInterval = videosDuration[indexPath.row] // 2 minutes, 30 seconds
        let formatter = DateComponentsFormatter()
        formatter.unitsStyle = .positional
        formatter.allowedUnits = [ .minute, .second ]
        formatter.zeroFormattingBehavior = [ .pad ]
        let formattedDuration = formatter.string(from: duration)
        cell.duration.text = "\(String(describing: formattedDuration!))"

        return cell
    }

    func grabPhotos(){
        let imgManager = PHImageManager.default()

        let requestOptions = PHImageRequestOptions()
        requestOptions.isSynchronous = false
        requestOptions.deliveryMode = .highQualityFormat

        let fetchOptions = PHFetchOptions()
        fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
        if let fetchResult : PHFetchResult = PHAsset.fetchAssets(with: .video, options: fetchOptions) {
            if fetchResult.count > 0 {
                for i in 0..<fetchResult.count{
                    //Used for fetch Image//
                    imgManager.requestImage(for: fetchResult.object(at: i) as PHAsset , targetSize: CGSize(width: 100, height: 100), contentMode: .aspectFill, options: requestOptions, resultHandler: {
                        image, error in
                        let imageOfVideo = image! as UIImage
                        self.photoLibrary.append(imageOfVideo)
                    })
                    //Used for fetch Video//
                    imgManager.requestAVAsset(forVideo: fetchResult.object(at: i) as PHAsset, options: PHVideoRequestOptions(), resultHandler: {(avAsset, audioMix, info) -> Void in
                        if let asset = avAsset as? AVURLAsset {
                            self.videoURL.append(asset.url)
                            let duration : CMTime = asset.duration
                            let durationInSecond = CMTimeGetSeconds(duration)
                            self.videosDuration.append(durationInSecond)
                        }
                    })
                }
            }
            else{
                //showAllertToImportImage()//A function to show alert
            }
        }

    }

Solution

  • Since photos are being fetched asynchronously so the order is not maintained in the images and videos array, so to get the corresponding image for the video you can use dictionaries to store the result

    I have made some modifications in your code, please check

    var imageDictionary = [String: AnyObject]()
    var videoDictionary = [String: AnyObject]()
    
    func fetchData(){
            let imgManager = PHImageManager.default()
    
            let requestOptions = PHImageRequestOptions()
            requestOptions.isSynchronous = false
            requestOptions.deliveryMode = .highQualityFormat
    
            let fetchOptions = PHFetchOptions()
            fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
            if let fetchResult : PHFetchResult = PHAsset.fetchAssets(with: .video, options: fetchOptions) {
                if fetchResult.count > 0 {
                    for i in 0..<fetchResult.count{
                        //Used for fetch Image//
    
                        imgManager.requestImage(for: fetchResult.object(at: i) as PHAsset , targetSize: CGSize(width: 100, height: 100), contentMode: .aspectFill, options: requestOptions, resultHandler: {
                            image, error in
                            let imageOfVideo = image! as UIImage
                            //self.photoLibrary.append(imageOfVideo)
                            let key = "\(i)"
                            self.imageDictionary[key] = imageOfVideo
                        })
                        //Used for fetch Video//
                        imgManager.requestAVAsset(forVideo: fetchResult.object(at: i) as PHAsset, options: PHVideoRequestOptions(), resultHandler: {(avAsset, audioMix, info) -> Void in
                            if let asset = avAsset as? AVURLAsset {
                                //let videoData = NSData(contentsOf: asset.url)
                                self.videoURL.append(asset.url)
                                //print(asset.url)
                                let duration : CMTime = asset.duration
                                let durationInSecond = CMTimeGetSeconds(duration)
                                //self.videosDuration.append(durationInSecond)
                                //print(durationInSecond)
                                let key = "\(i)"
                                self.videoDictionary[key] = ["VideoURL" : asset.url, "VideoDuration" : durationInSecond]
                            }
                        })
                    }
                }
                else{
                    //showAllertToImportImage()//A function to show alert
                }
            }
    
        }
    
    
        override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
            return self.imageDictionary.count
        }
    
        override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    
                   //You can access the values like this....
    
                   let key = "\(indexPath.row)"
    
                   cell.image = self.imageDictionary[key] as? UIImage
                   let singleVideo = self.videoDictionary[key] as? [String: AnyObject]
    
                   cell.videoURL = singleVideo["VideoURL"] as? URL
                   cell.videoDuration = singleVideo["VideoDuration"] as? CMTime
    
    
        }