Получить все фотографии из библиотеки на основе creationDate в Swift [Faster Way?] - PullRequest
0 голосов
/ 01 ноября 2018

У меня есть UICollectionView отображение фотографий библиотеки, основанных на последней дате создания. Для этого я использую код ниже:

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 секунд. До 6К фотографий нет проблем, но мне действительно нужен какой-то эффективный способ, чтобы я мог загрузить некоторые из asset в UICollectionView, а остальные я могу добавить позже. Мне нужно, чтобы независимо от того, сколько фотографий было, это должно занять не более 2 - 3 секунд. Кто-нибудь может помочь, пожалуйста?

1 Ответ

0 голосов
/ 01 ноября 2018

Допустим, у вас есть 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 для представления вашей коллекции, чтобы предварительно загрузить некоторые данные

EDIT: -

Я предполагаю, что вы уже попробовали следующее: -

        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)
}

Если это не поможет, как насчет перезагрузки представления коллекции с каким-либо обратным вызовом после каждой итерации цикла? (с вышеуказанным подходом) Таким образом, вы не заставите пользователя ждать, пока все это не загрузится

ИДК, это может выглядеть мелочно, но я просто пытаюсь помочь:)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...