【问题标题】:Fetch All Photos from Library based on creationDate in Swift [Faster Way?]基于 Swift 中的 creationDate 从库中获取所有照片 [更快的方式?]
【发布时间】:2018-11-01 09:22:56
【问题描述】:

我有一个UICollectionView 显示基于最新“creationDate”的图书馆照片。为此,我使用以下代码:

struct AssetsData {
    var creationDate: Date, assetResult: PHFetchResult<PHAsset>
}

func fetchPhotos() -> [AssetsData] {
    //Date Formatter
    let formatter = DateFormatter()
    formatter.dateStyle = DateFormatter.Style.medium
    formatter.timeStyle = DateFormatter.Style.none

    //Photos fetch
    let fetchOptions = PHFetchOptions()
    let sortOrder = [NSSortDescriptor(key: "creationDate", ascending: false)]
    fetchOptions.sortDescriptors = sortOrder
    let assetsFetchResult = PHAsset.fetchAssets(with: .image, options: fetchOptions)
    var arrCreationDate = [Date]()
    var arrDates = [String]()

    //Getting All dates
    for index in 0..<assetsFetchResult.count {
        if let creationDate = assetsFetchResult[index].creationDate {
            let formattedDate = formatter.string(from: creationDate)
            if !arrDates.contains(formattedDate) {
                arrDates.append(formattedDate)
                arrCreationDate.append(creationDate)
            }
        }
    }

    //Fetching Assets based on Dates
    var arrPhotoAssetsData = [AssetsData]()
    for createdDate in arrCreationDate {
        if let startDate = getDate(forDay: createdDate.day, forMonth: createdDate.month, forYear: createdDate.year, forHour: 0, forMinute: 0, forSecond: 0), let endDate = getDate(forDay: createdDate.day, forMonth: createdDate.month, forYear: createdDate.year, forHour: 23, forMinute: 59, forSecond: 59) {
            fetchOptions.predicate = NSPredicate(format: "creationDate > %@ AND creationDate < %@", startDate as NSDate, endDate as NSDate)
            let assetsPhotoFetchResult = PHAsset.fetchAssets(with: PHAssetMediaType.image, options: fetchOptions)
            arrPhotoAssetsData.append(AssetsData(creationDate: createdDate, assetResult: assetsPhotoFetchResult))
        }
    }
    return arrPhotoAssetsData
}

func getDate(forDay day: Int, forMonth month: Int, forYear year: Int, forHour hour: Int, forMinute minute: Int, forSecond second: Int) -> Date? {
    var dateComponents = DateComponents()
    dateComponents.day = day
    dateComponents.month = month
    dateComponents.year = year
    dateComponents.hour = hour
    dateComponents.minute = minute
    dateComponents.second = second
    var gregorian = Calendar(identifier: Calendar.Identifier.gregorian)
    gregorian.timeZone = NSTimeZone.system
    return gregorian.date(from: dateComponents)
}

代码运行良好!但问题是加载 10k+ 张照片需要将近 7 - 9 秒。直到 6k 张照片都没有问题,但我真的需要一些有效的方法,以便我可以在 UICollectionView 中加载一些 asset,其余的我可以稍后添加。我需要无论照片数量如何,都不应超过 2 - 3 秒。有人可以帮忙吗?

【问题讨论】:

    标签: swift phasset photosframework phassetslibrary


    【解决方案1】:

    假设您有 8k 张照片。因此,您遍历两个“for”循环以获取 arrCreationDate 和 arrPhotoAssets 数据(这是所需工作的两倍)

    相反,您可以尝试通过一个循环来完成。这是一个粗略的方法:-

        let assetsFetchResult = PHAsset.fetchAssets(with: .image, options: fetchOptions)
        let fetchOptions = PHFetchOptions()
        var arrCreationDate = [Date]()
        var arrPhotoAssetsData = [AssetsData]()
        var arrDates = [String]()
    
         for index in 0..<assetsFetchResult.count {
            if let creationDate = assetsFetchResult[index].creationDate {
                let formattedDate = formatter.string(from: creationDate)
                if !arrDates.contains(formattedDate) {
                    //You can convert the formattedDate to actual date here and do a check similar to this, do what you do in the other loop here too
                    if(actualDate < actualDateOfTheFirstElementAtArray){
                       arrCreationDate.insert(actualDate, at: 0)
                       fetchOptions.predicate = NSPredicate(format: "creationDate > %@ AND creationDate < %@", startDate as NSDate, endDate as NSDate)
                       let assetsPhotoFetchResult = PHAsset.fetchAssets(with: PHAssetMediaType.image, options: fetchOptions)
                       arrPhotoAssetsData.insert(AssetsData(creationDate: createdDate, assetResult: assetsPhotoFetchResult), at: 0)
                    }
                }
            }
         }
    

    这只是为了让您大致了解我在说什么,因为这样可以减轻一半的负担(只需一个循环)

    还可以尝试使用 prefetchDataSource 为您的集合视图预加载一些数据

    编辑:-

    我假设您已经尝试过以下方法:-

            func fetchPhotos() -> [AssetsData] {
        //Date Formatter
        let formatter = DateFormatter()
        formatter.dateStyle = DateFormatter.Style.medium
        formatter.timeStyle = DateFormatter.Style.none
    
        //Photos fetch
        let fetchOptions = PHFetchOptions()
        let sortOrder = [NSSortDescriptor(key: "creationDate", ascending: false)]
        fetchOptions.sortDescriptors = sortOrder
        let assetsFetchResult = PHAsset.fetchAssets(with: .image, options: fetchOptions)
        var arrCreationDate = [Date]()
        var arrDates = [String]()
        var arrPhotoAssetsData = [AssetsData]()
    
        //Getting All dates
        for index in 0..<assetsFetchResult.count {
            if let creationDate = assetsFetchResult[index].creationDate {
                let formattedDate = formatter.string(from: creationDate)
                if !arrDates.contains(formattedDate) {
                    arrDates.append(formattedDate)
                    arrCreationDate.append(creationDate)
                    convertToAssetsDataAndAppend(date: creationDate, fetchOptions: fetchOptions, toArray: &arrPhotoAssetsData)
                }
            }
        }
        return arrPhotoAssetsData
        }
    
        func convertToAssetsDataAndAppend(date: Date, fetchOptions: PHFetchOptions, toArray: inout [AssetsData]){
        if let startDate = getDate(forDay: date.day, forMonth: date.month, forYear: date.year, forHour: 0, forMinute: 0, forSecond: 0), let endDate = getDate(forDay: date.day, forMonth: date.month, forYear: date.year, forHour: 23, forMinute: 59, forSecond: 59) {
            fetchOptions.predicate = NSPredicate(format: "creationDate > %@ AND creationDate < %@", startDate as NSDate, endDate as NSDate)
            let assetsPhotoFetchResult = PHAsset.fetchAssets(with: PHAssetMediaType.image, options: fetchOptions)
            toArray.append(AssetsData(creationDate: date, assetResult: assetsPhotoFetchResult))
        }
    }
    
    func getDate(forDay day: Int, forMonth month: Int, forYear year: Int, forHour hour: Int, forMinute minute: Int, forSecond second: Int) -> Date? {
        var dateComponents = DateComponents()
        dateComponents.day = day
        dateComponents.month = month
        dateComponents.year = year
        dateComponents.hour = hour
        dateComponents.minute = minute
        dateComponents.second = second
        var gregorian = Calendar(identifier: Calendar.Identifier.gregorian)
        gregorian.timeZone = NSTimeZone.system
        return gregorian.date(from: dateComponents)
    }
    

    如果这没有帮助,那么在每次循环迭代后使用某种回调重新加载集合视图怎么样? (使用上述方法) 这样,您不会让用户等到整个内容加载完毕

    Idk,这些可能看起来很琐碎,但我只是想帮忙:)

    【讨论】:

    • 谢谢。我试过了,但仍然需要 2.09 秒才能获取 3.1k 张照片,而我的需要 1.85 秒。
    • 酷,所以你在当前的 for 循环中使用了另一个循环来对元素进行排序?在这种情况下,它是一个嵌套循环,显然会比通常的两个单独循环消耗更多时间。您可以做的是尝试使用二进制搜索进行排序
    • 嗯..二进制搜索不会像我想的那样有效,因为没有搜索特定日期。我们需要根据从库中获取的日期的所有数据。你怎么看?
    • 不,我明白了,但我认为您采用的方法类似于我的想法,我的错。我已经编辑了答案。让我知道你的想法...
    • 所以你只是区分了附加部分!太酷了,我会尝试这种方式,比如附加前 5 或 6 个日期,然后重新加载 UICollectionView,然后再次附加新数据重新加载它。
    猜你喜欢
    • 2018-08-29
    • 2016-10-29
    • 1970-01-01
    • 2016-11-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多