46

How do I get a list of all collections, including the camera roll (which is now called moments), in iOS8?

In iOS 7, I use ALAssetGroup enumeration block, but that doesn't include iOS moments which is seems to be equivalent to Camera Roll in iOS7.

    void (^assetGroupEnumerator)(ALAssetsGroup *, BOOL *) = ^(ALAssetsGroup *group, BOOL *stop)
    {
        if (group == nil) {// We're done enumerating
            return;
        }

        [group setAssetsFilter:[ALAssetsFilter allAssets]];
        if ([[sGroupPropertyName lowercaseString] isEqualToString:@"camera roll"] && nType == ALAssetsGroupSavedPhotos) {
            [_assetGroups insertObject:group atIndex:0];
        } else {
            [_assetGroups addObject:group];
        }
    };

    // Group Enumerator Failure Block
    void (^assetGroupEnumberatorFailure)(NSError *) = ^(NSError *error) {
        SMELog(@"Enumeration occured %@", [error description]);
    };

    // Enumerate Albums
    [_library enumerateGroupsWithTypes:kSupportedALAlbumsMask
                            usingBlock:assetGroupEnumerator
                          failureBlock:assetGroupEnumberatorFailure];
    }];
kev
  • 7,712
  • 11
  • 30
  • 41

3 Answers3

125

Using Photos Framework is a bit different, you can achieve the same result though, you just have to do it in parts.

1) Get all photos (Moments in iOS8, or Camera Roll before)

PHFetchResult *allPhotosResult = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:nil];

Optionally if you want them ordered as by creation date, you just add PHFetchOptions like so:

PHFetchOptions *allPhotosOptions = [PHFetchOptions new];
allPhotosOptions.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]];

PHFetchResult *allPhotosResult = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:allPhotosOptions];

Now if you want you can get assets from the PHFetchResult object:

[allPhotosResult enumerateObjectsUsingBlock:^(PHAsset *asset, NSUInteger idx, BOOL *stop) {
    NSLog(@"asset %@", asset);
}];

2) Get all user albums (with additional sort for example to only show albums with at least one photo)

PHFetchOptions *userAlbumsOptions = [PHFetchOptions new];
userAlbumsOptions.predicate = [NSPredicate predicateWithFormat:@"estimatedAssetCount > 0"];

PHFetchResult *userAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAny options:userAlbumsOptions];

[userAlbums enumerateObjectsUsingBlock:^(PHAssetCollection *collection, NSUInteger idx, BOOL *stop) {
        NSLog(@"album title %@", collection.localizedTitle);
}];

For each PHAssetCollection that is returned from PHFetchResult *userAlbums you can fetch PHAssets like so (you can even limit results to include only photo assets):

PHFetchOptions *onlyImagesOptions = [PHFetchOptions new];
onlyImagesOptions.predicate = [NSPredicate predicateWithFormat:@"mediaType = %i", PHAssetMediaTypeImage];
PHFetchResult *result = [PHAsset fetchAssetsInAssetCollection:collection options:onlyImagesOptions];

3) Get smart albums

PHFetchResult *smartAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil];
[smartAlbums enumerateObjectsUsingBlock:^(PHAssetCollection *collection, NSUInteger idx, BOOL *stop) {
        NSLog(@"album title %@", collection.localizedTitle);
}];

One thing to note with Smart Albums is that collection.estimatedAssetCount can return NSNotFound if estimatedAssetCount cannot be determined. As title suggest this is estimated. If you want to be sure of number of assets you have to perform fetch and get the count like:

PHFetchResult *assetsFetchResult = [PHAsset fetchAssetsInAssetCollection:assetCollection options:nil];

number of assets = assetsFetchResult.count

Apple has a sample project that does what you want:

https://developer.apple.com/library/content/samplecode/UsingPhotosFramework/ExampleappusingPhotosframework.zip (you have to be a registered developer to access this)

Cœur
  • 37,241
  • 25
  • 195
  • 267
Ladislav
  • 7,223
  • 5
  • 27
  • 31
  • 1
    Hey @Ladislav, great answer. But how did you know to use `creationDate` for the sortDescriptor, are the photo attributes documented anywhere? – snowbound Jul 15 '15 at 03:35
  • 3
    There is a very nice documentation by Apple on this - https://developer.apple.com/library/prerelease/ios/documentation/Photos/Reference/PHFetchOptions_Class/index.html#//apple_ref/doc/uid/TP40014396-CH1-SW5 Take a look at sortDescriptors and you will see: "Construct sort descriptors with the properties of the photo entities that you want to fetch, listed in Table 1. For example, the following code sorts by creation date to find the oldest asset in the photo library." Table 1 has all properties that you can use for PHAsset, PHAssetCollection, PHCollectionList, PHCollection... – Ladislav Jul 17 '15 at 17:59
  • Thanks. Exactly what I was looking for! – snowbound Jul 20 '15 at 06:38
  • @Ladislav Thanks for the great answer. I don't understand though why you need to fetch "All Photos". I realize it does this in the sample app, but if you fetch the smart albums and albums, won't it cover all photos? – Ser Pounce Aug 18 '15 at 07:53
  • I tried to get all photos, but it doesn't include the photos in Moments (it only include Camera Roll). Not sure it got to do with iOS 9. I also tried Apple sample code, and while they say it includes moments, it doesn't. – samwize Dec 22 '15 at 12:35
  • @samwize explain to me the difference between Moments and what you call Camera Roll? – Ladislav Dec 22 '15 at 16:20
  • @Ladislav I am using iOS 9, and in my Moments, it includes the photos that I synced with iTunes (Photos app). Moments includes everything, while Camera Roll includes only the photos that took and not yet imported and deleted. – samwize Dec 22 '15 at 16:26
  • @samwize try playing with number 3) Get Smart Albums by changing subtype: Check enum options for PHAssetCollectionSubtype: https://developer.apple.com/library/ios/documentation/Photos/Reference/PHAssetCollection_Class/#//apple_ref/c/tdef/PHAssetCollectionSubtype – Ladislav Dec 22 '15 at 16:43
  • @Ladislav I tried, and doesn't work. The following code from http://stackoverflow.com/a/25996662/242682 does work, although it is kind of brute force by iterating all the assets. – samwize Dec 22 '15 at 16:46
  • Fails if estimatedAssetCount == NSNotFound – jjxtra Jan 25 '16 at 16:07
19

This is simply a translation of @Ladislav's superb accepted answer into Swift:

// *** 1 ***
// Get all photos (Moments in iOS8, or Camera Roll before)
// Optionally if you want them ordered as by creation date, you just add PHFetchOptions like so:
let allPhotosOptions = PHFetchOptions()
allPhotosOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: true)]

let allPhotosResult = PHAsset.fetchAssetsWithMediaType(PHAssetMediaType.Image, options: allPhotosOptions)

// Now if you want you can get assets from the PHFetchResult object:
allPhotosResult.enumerateObjectsUsingBlock({ print("Asset \($0.0)") })

// *** 2 ***
// Get all user albums (with additional sort for example to only show albums with at least one photo)
let userAlbumsOptions = PHFetchOptions()
userAlbumsOptions.predicate = NSPredicate(format: "estimatedAssetCount > 0")

let userAlbums = PHAssetCollection.fetchAssetCollectionsWithType(PHAssetCollectionType.Album, subtype: PHAssetCollectionSubtype.Any, options: userAlbumsOptions)

userAlbums.enumerateObjectsUsingBlock({
    if let collection = $0.0 as? PHAssetCollection {
        print("album title: \(collection.localizedTitle)")
        //For each PHAssetCollection that is returned from userAlbums: PHFetchResult you can fetch PHAssets like so (you can even limit results to include only photo assets):
        let onlyImagesOptions = PHFetchOptions()
        onlyImagesOptions.predicate = NSPredicate(format: "mediaType = %i", PHAssetMediaType.Image.rawValue)
        if let result = PHAsset.fetchKeyAssetsInAssetCollection(collection, options: onlyImagesOptions) {
            print("Images count: \(result.count)")
        }
    }
})

// *** 3 ***
// Get smart albums
let smartAlbums = PHAssetCollection.fetchAssetCollectionsWithType(.SmartAlbum, subtype: .AlbumRegular, options: nil) // Here you can specify Photostream, etc. as PHAssetCollectionSubtype.xxx
smartAlbums.enumerateObjectsUsingBlock( {
    if let assetCollection = $0.0 as? PHAssetCollection {
        print("album title: \(assetCollection.localizedTitle)")
        // One thing to note with Smart Albums is that collection.estimatedAssetCount can return NSNotFound if estimatedAssetCount cannot be determined. As title suggest this is estimated. If you want to be sure of number of assets you have to perform fetch and get the count like:

        let assetsFetchResult = PHAsset.fetchAssetsInAssetCollection(assetCollection, options: nil)
        let numberOfAssets = assetsFetchResult.count
        let estimatedCount =  (assetCollection.estimatedAssetCount == NSNotFound) ? -1 : assetCollection.estimatedAssetCount
        print("Assets count: \(numberOfAssets), estimate: \(estimatedCount)")
    }
})
Ilias Karim
  • 4,798
  • 3
  • 38
  • 60
Grimxn
  • 22,115
  • 10
  • 72
  • 85
  • Thank you so much! – Shawn Baek Aug 19 '17 at 07:26
  • Hi @Grimxn, May i know, How to fetch the Last import photos only in the Photo library. Based on creation date sorting also we are getting all the images. But my requirement is only fetch the last imported photos. because, We used external camera and take the photos and copy to IPAD. So, How to fetch the "Last Import photos" in the app end? – lakshmanbob Sep 04 '17 at 08:59
  • I'm not working on this at the moment, but I guess that you need to enumerate the asset collections `https://developer.apple.com/documentation/photos/phassetcollection#1656287` and check their `assetCollectionType`s... – Grimxn Sep 04 '17 at 12:23
1

Try this Code...

self.imageArray=[[NSArray alloc] init];

PHImageRequestOptions *requestOptions = [[PHImageRequestOptions alloc] init];
requestOptions.resizeMode   = PHImageRequestOptionsResizeModeExact;
requestOptions.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
requestOptions.synchronous = true;
PHFetchResult *result = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:nil];

NSLog(@"%d",(int)result.count);

    PHImageManager *manager = [PHImageManager defaultManager];
    NSMutableArray *images = [NSMutableArray arrayWithCapacity:countValue];

    // assets contains PHAsset objects.

    __block UIImage *ima;
    for (PHAsset *asset in result) {
        // Do something with the asset

        [manager requestImageForAsset:asset
                           targetSize:PHImageManagerMaximumSize
                          contentMode:PHImageContentModeDefault
                              options:requestOptions
                        resultHandler:^void(UIImage *image, NSDictionary *info) {
                            ima = image;

                            [images addObject:ima];

                        }];




    self.imageArray = [images copy];