HowTo get Photos from iOS assets-library sorted by lastModified ?
-
no problem to get the assets-library - directory and iterate over entries
QDir theDir = QDir("assets-library://"); QFileInfoList entryList = theDir.entryInfoList(); for (int i = 0; i < entryList.size(); ++i) { QFileInfo fileInfo = entryList.at(i); // fileInfo.filePath() is something like this: // "assets-library://asset/asset.JPG?id=01DC0CE7-0A57-4B42-BF2E-9A5719D04E1D&ext=JPG"
having access to the Photos from QML or C++ and display as Image - no problem
unfortunately the Photos are not sorted
fileInfo.lastModified()
andfileInfo.created()
fromQFileInfo
are empty, so I cannot do a sort from C++I know that there's a way to use the native iOS ImagePicker:
import Qt.labs.platform 1.0 // hint: also tried FileDialog from QtQuick.Dialogs 1.2 // but J-P told me QtQuick.Dialogs FileDialog is a 'Monster' - I should use from labs.platform FileDialog { fileMode: FileDialog.OpenFiles property var standardPicturesLocations: StandardPaths.standardLocations(StandardPaths.PicturesLocation) folder: standardPicturesLocations[standardPicturesLocations.length - 1] }
but this native Picker only allows us to pick single Photos
https://bugreports.qt.io/browse/QTBUG-67874But I need multi-selection of files, so I created a GridView to preview thumbnails and to select one or more. This works fine but users are complaining that the Photos are not sorted.
Seems I need som iOS native code to get a sorted Array of Pathes to Photos from assets-library.
Looked at StackOverFlow and found nothing I really understand with less knowledge on native iOS / Obj-C development.Anyone has solved this or any ideas HowTo get the list of Photos from iOS sorted ?
(will provide the solution together with my nearly ready-to-go QML Camera App for Android / iOS)
THANKS
-
@SGaist here's my solution
.pro:
ios { LIBS += -framework Photos
cpp: (.mm)
#import <Photos/Photos.h> const int MEDIA_PHOTOS = 1; const int MEDIA_VIDEOS = 2; #pragma unused (MEDIA_VIDEOS) const QString MEDIA_PHOTO_SUFFIX = "JPG"; const QString MEDIA_VIDEO_SUFFIX = "MP4"; ... QVariantMap IosUtil::fetchAssets(const int mediaType) { QVariantMap albumsWithSortedAssetsMap; QMap<QDateTime, QString> assetsMap; // get the smart albums PHFetchResult *smartAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil]; for (NSUInteger i = 0; i < smartAlbums.count; i++) { PHAssetCollection *assetCollection = smartAlbums[i]; // the smart album name QString theAlbum = QString::fromNSString(assetCollection.localizedTitle); // get photo collections for this album PHFetchResult *assetsFetchResult = [PHAsset fetchAssetsInAssetCollection:assetCollection options:nil]; // to get it sorted by modificationDate we use a Map assetsMap.clear(); // get the Assets (Photos or Videos) for (NSUInteger x = 0; x < assetsFetchResult.count; x ++) { PHAsset *asset = assetsFetchResult[x]; int mt = asset.mediaType; if(mt != mediaType) { continue; } QString theSuffix; if(mediaType == MEDIA_PHOTOS) { theSuffix = MEDIA_PHOTO_SUFFIX; } else { theSuffix = MEDIA_VIDEO_SUFFIX; } QDateTime modificationDate = QDateTime::fromNSDate(asset.modificationDate); // now constructing the URL // https://stackoverflow.com/questions/42579544/ios-phassetcollection-localizedtitle-always-returning-english-name // https://stackoverflow.com/questions/28887638/how-to-get-an-alasset-url-from-a-phasset // AF3C1B68-222B-43D3-9478-53097429F1B2/L0/001 becomes // assets-library://asset/asset.JPG?id=AF3C1B68-222B-43D3-9478-53097429F1B2&ext=JPG QString theId = QString::fromNSString(asset.localIdentifier); QString theUrl = "assets-library://asset/asset."; theUrl.append(theSuffix).append("?id="); theUrl.append(theId.split('/').first()); theUrl.append("&ext=").append(theSuffix); // qDebug() << "URL: " << theUrl; assetsMap.insert(modificationDate, theUrl); } // assets in collection // now we can fill our list of assets-URLs (sorted descendend by modificationDate) QStringList sortedAssetsList; QMapIterator<QDateTime, QString> assetsIterator(assetsMap); assetsIterator.toBack(); while (assetsIterator.hasPrevious()) { assetsIterator.previous(); sortedAssetsList.append(assetsIterator.value()); } // insert sorted list of URLLs into albums map albumsWithSortedAssetsMap.insert(theAlbum, sortedAssetsList); } // media collections in smart album // now we have all albums sorted by album name together with sorted collections of assets, where the newest comes first return albumsWithSortedAssetsMap; }
Good to know: if you fetch the assets, then switch to iOS Camera and create new Photos or delete Photos, go back to your App and again call the same method to fetch assets you’ll get the old content. I had to create a new instance of my IosUtil class to be able to get the newest data from Phto Library.
-
Hi,
Can you also share the stack overflow answers that could be useful ? It might be easier with several pair of eyes to get something up and running.
-
@SGaist good idea. will add this later today or tomorrow.
-
@SGaist here's what I found in StackOverFlow
https://stackoverflow.com/questions/7977664/sort-files-by-creation-date-ios#
don't know if this would work with assets (Photos)--
https://stackoverflow.com/questions/30602830/phfetchresult-get-all-photos-and-sort-by-date-inconsistent/30603715
seems this does what I want, but how to implement that in a way toget an array of pathes for the Photos--
would be cool if someone has an idea HowTo combine this with Qt C++ to get a sorted list of pathes from "assets-library://"
and what must be put into .h and what into .mm to call it from Qtthanks for any tips and hints
-
@ekkescorner anyone ideas ?
otherwise next week I have to start fighting with Obj-C to make it work ;-) -
figured it out to get iOS Photos sorted for multi-select. will provide src later
-
Sorry, I lost track of the thread.
Glad you found out !
-
@SGaist here's my solution
.pro:
ios { LIBS += -framework Photos
cpp: (.mm)
#import <Photos/Photos.h> const int MEDIA_PHOTOS = 1; const int MEDIA_VIDEOS = 2; #pragma unused (MEDIA_VIDEOS) const QString MEDIA_PHOTO_SUFFIX = "JPG"; const QString MEDIA_VIDEO_SUFFIX = "MP4"; ... QVariantMap IosUtil::fetchAssets(const int mediaType) { QVariantMap albumsWithSortedAssetsMap; QMap<QDateTime, QString> assetsMap; // get the smart albums PHFetchResult *smartAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil]; for (NSUInteger i = 0; i < smartAlbums.count; i++) { PHAssetCollection *assetCollection = smartAlbums[i]; // the smart album name QString theAlbum = QString::fromNSString(assetCollection.localizedTitle); // get photo collections for this album PHFetchResult *assetsFetchResult = [PHAsset fetchAssetsInAssetCollection:assetCollection options:nil]; // to get it sorted by modificationDate we use a Map assetsMap.clear(); // get the Assets (Photos or Videos) for (NSUInteger x = 0; x < assetsFetchResult.count; x ++) { PHAsset *asset = assetsFetchResult[x]; int mt = asset.mediaType; if(mt != mediaType) { continue; } QString theSuffix; if(mediaType == MEDIA_PHOTOS) { theSuffix = MEDIA_PHOTO_SUFFIX; } else { theSuffix = MEDIA_VIDEO_SUFFIX; } QDateTime modificationDate = QDateTime::fromNSDate(asset.modificationDate); // now constructing the URL // https://stackoverflow.com/questions/42579544/ios-phassetcollection-localizedtitle-always-returning-english-name // https://stackoverflow.com/questions/28887638/how-to-get-an-alasset-url-from-a-phasset // AF3C1B68-222B-43D3-9478-53097429F1B2/L0/001 becomes // assets-library://asset/asset.JPG?id=AF3C1B68-222B-43D3-9478-53097429F1B2&ext=JPG QString theId = QString::fromNSString(asset.localIdentifier); QString theUrl = "assets-library://asset/asset."; theUrl.append(theSuffix).append("?id="); theUrl.append(theId.split('/').first()); theUrl.append("&ext=").append(theSuffix); // qDebug() << "URL: " << theUrl; assetsMap.insert(modificationDate, theUrl); } // assets in collection // now we can fill our list of assets-URLs (sorted descendend by modificationDate) QStringList sortedAssetsList; QMapIterator<QDateTime, QString> assetsIterator(assetsMap); assetsIterator.toBack(); while (assetsIterator.hasPrevious()) { assetsIterator.previous(); sortedAssetsList.append(assetsIterator.value()); } // insert sorted list of URLLs into albums map albumsWithSortedAssetsMap.insert(theAlbum, sortedAssetsList); } // media collections in smart album // now we have all albums sorted by album name together with sorted collections of assets, where the newest comes first return albumsWithSortedAssetsMap; }
Good to know: if you fetch the assets, then switch to iOS Camera and create new Photos or delete Photos, go back to your App and again call the same method to fetch assets you’ll get the old content. I had to create a new instance of my IosUtil class to be able to get the newest data from Phto Library.
-
Thank you very much !
-
the code above only fetches smart albums. if you also need albums created manually from user, you must fetch them, too:
PHFetchResult *userCollections = [PHCollectionList fetchTopLevelUserCollectionsWithOptions:nil];
and if you need sync’d albums:
PHFetchResult *syncedAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAlbumSyncedAlbum options:nil];
user collections tested, sync’d collections not tested yet because I don’t need them