I have seen other apps do it where you can import the last photo from the Photos app for quick use but as far as I know, I only know how to get A image and not the last (most recent one). Can anyone show me how to get the last image?
13 Answers
This code snippet will get the latest image from the camera roll (iOS 7 and below):
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
// Enumerate just the photos and videos group by using ALAssetsGroupSavedPhotos.
[library enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
// Within the group enumeration block, filter to enumerate just photos.
[group setAssetsFilter:[ALAssetsFilter allPhotos]];
// Chooses the photo at the last index
[group enumerateAssetsWithOptions:NSEnumerationReverse usingBlock:^(ALAsset *alAsset, NSUInteger index, BOOL *innerStop) {
// The end of the enumeration is signaled by asset == nil.
if (alAsset) {
ALAssetRepresentation *representation = [alAsset defaultRepresentation];
UIImage *latestPhoto = [UIImage imageWithCGImage:[representation fullScreenImage]];
// Stop the enumerations
*stop = YES; *innerStop = YES;
// Do something interesting with the AV asset.
[self sendTweet:latestPhoto];
}
}];
} failureBlock: ^(NSError *error) {
// Typically you should handle an error more gracefully than this.
NSLog(@"No groups");
}];
iOS 8 and above:
PHFetchOptions *fetchOptions = [[PHFetchOptions alloc] init];
fetchOptions.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]];
PHFetchResult *fetchResult = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:fetchOptions];
PHAsset *lastAsset = [fetchResult lastObject];
[[PHImageManager defaultManager] requestImageForAsset:lastAsset
targetSize:self.photoLibraryButton.bounds.size
contentMode:PHImageContentModeAspectFill
options:PHImageRequestOptionsVersionCurrent
resultHandler:^(UIImage *result, NSDictionary *info) {
dispatch_async(dispatch_get_main_queue(), ^{
[[self photoLibraryButton] setImage:result forState:UIControlStateNormal];
});
}];

- 12,376
- 22
- 105
- 191
-
If the latest photo was edited by user and saved, this code can only get the original photo. Anyone knows how to get the latest photo in its edited form? – an0 Aug 17 '12 at 22:55
-
I checked my vote history and I'm sure I did not down vote your answer. – an0 Aug 18 '12 at 14:20
-
Ok that's fine I just wanted to know why someone downvoted it – SimplyKiwi Aug 18 '12 at 15:31
-
11Note: this code will crash if there are no photos in the camera roll. Uncommon in the real world, but still something you'd want to check for. I added `if ([group numberOfAssets] < 1) return;` inside the first block level to defend against this. – Kenny Winker Dec 04 '12 at 03:52
-
Thanks, I think I remember running into this issue and doing something similar but I forgot to update the question. Thanks again for contributing to this answer. – SimplyKiwi Dec 04 '12 at 05:22
-
The crash which @KennyWinker wrote about has been fixed (by using a different `enumerateAssets…` method). – rastersize Feb 18 '14 at 13:15
-
@rastersize What is the method you are referring to? – SimplyKiwi Feb 18 '14 at 19:05
-
2@iBradApps `enumerateAssetsAtIndexes:options:usingBlock:` was replaced with `enumerateAssetsWithOptions:usingBlock:` and the option `NSEnumerationReverse`. With the addition of using the `stop` and `innerStop` boolean variables so we’ll still stop enumerating once an asset has been found. You can see the diff at http://stackoverflow.com/posts/8872425/revisions – rastersize Mar 17 '14 at 10:41
-
What's to stop the iOS8 code from returning a video PHAsset? – jlw Sep 23 '14 at 14:22
-
hmm good point @jlw, I just got this from the apple site. I am still not acquainted fully with PHAsset but maybe there is some way to filter it out to only photos? – SimplyKiwi Sep 23 '14 at 17:47
-
1[PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:fetchOptions]; ;) – jlw Sep 23 '14 at 17:49
-
Thanks @jlw I incorporated it into my answer! – SimplyKiwi Sep 24 '14 at 18:11
-
@iBradApps Oh actually, shouldn't result.lastObject be fetchResult.lastObject? – Jonathan Aquino Oct 07 '14 at 15:12
-
3Oh - hm - has the iOS 8 code been tested? I don't think that PHFetchResult.lastObject returns a UIImage? – Jonathan Aquino Oct 07 '14 at 18:51
-
@JonathanAquino yes that seems to be corrected now. it does in fact return an image, the 3rd line filters that out. And it works in my app! :) – SimplyKiwi Oct 08 '14 at 05:41
-
2PHAsset object no has data of image its only "meta-data" of image! – dev.nikolaz Oct 11 '14 at 18:19
-
@dev.nikolaz I believe that is incorrect, my code works fine for me without issues. – SimplyKiwi Oct 15 '14 at 04:10
-
2That fetchResult.lastObject returns an image for you is quite odd. The documentation suggests it should return a PHAsset, which should then be used to grab an image and supports various options (size, version, etc). I have no idea how this returns a UIImage for you and a PHAsset for me. I've added an answer that includes the PHAsset approach for interested parties (@an0) – isaac Oct 15 '14 at 19:11
-
Check @isaac 's answer for iOS8. – Tarek Hallak Nov 11 '14 at 16:06
-
I second Isaac, PHFetchResult is NOT an array of UIImages, but PHAssets (which are metadata). – Morkrom Nov 19 '14 at 17:34
-
The answer from @isaac for iOS 8+ is the correct one. It should be fixed in the answer to include the changes suggested by him. – Jão Dec 12 '14 at 06:04
-
@isaac I fixed my answer to incorporate your code since others have indicated that my code was incorrect. Thanks for the input! – SimplyKiwi Dec 13 '14 at 02:29
Great answer from iBrad, worked almost perfectly for me. The exception being that it was returning images at their original orientation (eg. upside down, -90°, etc).
To fix this I simply changed fullResolutionImage
to fullScreenImage
.
Here:
UIImage *latestPhoto = [UIImage imageWithCGImage:[representation fullScreenImage]];
It now works a treat.

- 1,028
- 13
- 24
-
3Thanks for the suggestion, I up-voted your answer and incorporated it into my answer as well! :) – SimplyKiwi Aug 17 '12 at 17:42
-
Well done for your suggestion Liam, its good to contribute to the world of Stack! – Justin Erswell Aug 17 '12 at 23:27
-
4This works if you're just displaying the image, but you don't actually get the full resolution image, just one that's appropriate for display on the user's device. If you need a full size image, you'd want to use `UIImage *latestPhoto = [UIImage imageWithCGImage:[representation fullResolutionImage] scale:[representation scale] orientation:[representation orientation]];` – Nate Cook Sep 18 '13 at 03:54
-
1@NateCook Doing that gives me a warning: "Implicit conversion from enumeration type 'ALAssetOrientation' (aka 'enum ALAssetOrientation') to different enumeration type 'UIImageOrientation' (aka 'enum UIImag...')". It may be better to do what is described here: http://biasedbit.com/alasset-image-orientation/ – Jonathan Aquino Sep 11 '14 at 01:48
-
1
iBrad's example includes an iOS8 snippet that apparently works, but I found myself confused by the return type he described. Here is a snippet that grabs the last image, including options for version and size requirements.
Of note are the ability to request a specific version (original, current) and size. In my case, as I wish to apply the returned image to a button, I request it sized and scaled to fit the button I'm applying it to:
PHFetchOptions *fetchOptions = [[PHFetchOptions alloc] init];
fetchOptions.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]];
PHFetchResult *fetchResult = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:fetchOptions];
PHAsset *lastAsset = [fetchResult lastObject];
[[PHImageManager defaultManager] requestImageForAsset:lastAsset
targetSize:self.photoLibraryButton.bounds.size
contentMode:PHImageContentModeAspectFill
options:PHImageRequestOptionsVersionCurrent
resultHandler:^(UIImage *result, NSDictionary *info) {
dispatch_async(dispatch_get_main_queue(), ^{
[[self photoLibraryButton] setImage:result forState:UIControlStateNormal];
});
}];

- 4,867
- 1
- 21
- 31
-
PHImageRequestOptions is an object. Can you actually pass a enum value "PHImageRequestOptionsVersionCurrent" instead? I think what you want is: PHImageRequestOptions* options = [PHImageRequestOptions new]; options.version = PHImageRequestOptionsVersionCurrent; – pronebird Feb 18 '15 at 11:30
Well, here is a solution of how to load last image from gallery with Swift 3 guys:
func loadLastImageThumb(completion: @escaping (UIImage) -> ()) {
let imgManager = PHImageManager.default()
let fetchOptions = PHFetchOptions()
fetchOptions.fetchLimit = 1
fetchOptions.sortDescriptors = [NSSortDescriptor(key:"creationDate", ascending: true)]
let fetchResult = PHAsset.fetchAssets(with: PHAssetMediaType.image, options: fetchOptions)
if let last = fetchResult.lastObject {
let scale = UIScreen.main.scale
let size = CGSize(width: 100 * scale, height: 100 * scale)
let options = PHImageRequestOptions()
imgManager.requestImage(for: last, targetSize: size, contentMode: PHImageContentMode.aspectFill, options: options, resultHandler: { (image, _) in
if let image = image {
completion(image)
}
})
}
}
If you need more speed, you can also use PHImageRequestOptions
and set those:
options.deliveryMode = .fastFormat
options.resizeMode = .fast
And this is the way you get it in your viewController (you should replace GalleryManager.manager with your class):
GalleryManager.manager.loadLastImageThumb { [weak self] (image) in
DispatchQueue.main.async {
self?.galleryButton.setImage(image, for: .normal)
}
}

- 1,751
- 17
- 24
Thanks for your answer iBrad Apps.
Just wanted to point out an error prevention for the special case when user has no images on his/her photo roll (strange case I know):
// Within the group enumeration block, filter to enumerate just photos.
[group setAssetsFilter:[ALAssetsFilter allPhotos]];
//Check that the group has more than one picture
if ([group numberOfAssets] > 0) {
// Chooses the photo at the last index
[group enumerateAssetsAtIndexes:[NSIndexSet indexSetWithIndex:([group numberOfAssets] - 1)] options:0 usingBlock:^(ALAsset *alAsset, NSUInteger index, BOOL *innerStop) {
// The end of the enumeration is signaled by asset == nil.
if (alAsset) {
ALAssetRepresentation *representation = [alAsset defaultRepresentation];
UIImage *latestPhoto = [UIImage imageWithCGImage:[representation fullScreenImage]];
[self.libraryButton setImage:latestPhoto forState:UIControlStateNormal];
}
}];
}
else {
//Handle this special case
}

- 1,605
- 11
- 21
Refer to answer by Liam. fullScreenImage
will return a scaled image fitting your device's screen size. For getting the actual image size:
ALAssetRepresentation *representation = [alAsset defaultRepresentation];
ALAssetOrientation orientation = [representation orientation];
UIImage *latestPhoto = [UIImage imageWithCGImage:[representation fullResolutionImage] scale:[representation scale] orientation:(UIImageOrientation)orientation];
Quoting Apple's ALAssetRepresentation Class Reference on fullResolutionImage
:
To create a correctly-rotated UIImage object from the CGImage, you use imageWithCGImage:scale:orientation: or initWithCGImage:scale:orientation:, passing the values of orientation and scale.

- 1,784
- 16
- 14
-
Good point, although I just wanted the Screen sized image in my case. I'm sure this will help others... thanks! – Liam Jun 17 '13 at 11:55
I found a typo that I'm embarrassed to admit to me longer than it should have to figure out. Maybe it will save someone else some time.
This line was missing a colon after indexSetWithIndex
:
[group enumerateAssetsAtIndexes:[NSIndexSet indexSetWithIndex:[group numberOfAssets] - 1]options:0 usingBlock:^(ALAsset *alAsset, NSUInteger index, BOOL *innerStop) {

- 532
- 1
- 7
- 18
Building upon iBrad's answer, here's a quick & dirty Swift version that works for me in iOS 8.1:
let imgManager = PHImageManager.defaultManager()
var fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key:"creationDate", ascending: true)]
if let fetchResult = PHAsset.fetchAssetsWithMediaType(PHAssetMediaType.Image, options: fetchOptions) {
imgManager.requestImageForAsset(fetchResult.lastObject as PHAsset, targetSize: self.destinationImageView.frame.size, contentMode: PHImageContentMode.AspectFill, options: nil, resultHandler: { (image, _) in
self.destinationImageView.image = image
})
}
Note: this requires iOS 8.0+. Be sure to link the Photos framework and add "import Photos" in your file.

- 2,667
- 3
- 30
- 37
Here is a version in Swift which requests the data and converts it to an UIImage, as the provided version returned an empty UIImage every time
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: true)]
let fetchResult = PHAsset.fetchAssetsWithMediaType(PHAssetMediaType.Image, options: fetchOptions)
if let lastAsset: PHAsset = fetchResult.lastObject as? PHAsset {
let manager = PHImageManager.defaultManager()
let imageRequestOptions = PHImageRequestOptions()
manager.requestImageDataForAsset(lastAsset, options: imageRequestOptions) {
(let imageData: NSData?, let dataUTI: String?,
let orientation: UIImageOrientation,
let info: [NSObject : AnyObject]?) -> Void in
if let imageDataUnwrapped = imageData, lastImageRetrieved = UIImage(data: imageDataUnwrapped) {
// do stuff with image
}
}
}

- 15,628
- 6
- 82
- 76

- 1,113
- 11
- 15
Heres a combination of iBrad's & Javier's answers (which worked great), but I am getting the thumbnail asset instead of the full resolution image. Some others may find this handy.
- (void)setCameraRollImage {
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
[group setAssetsFilter:[ALAssetsFilter allPhotos]];
if ([group numberOfAssets] > 0) {
// Chooses the photo at the last index
[group enumerateAssetsAtIndexes:[NSIndexSet indexSetWithIndex:([group numberOfAssets] - 1)] options:0 usingBlock:^(ALAsset *alAsset, NSUInteger index, BOOL *innerStop) {
// The end of the enumeration is signaled by asset == nil.
if (alAsset) {
UIImage *latestPhoto = [UIImage imageWithCGImage:[alAsset thumbnail]];
[self.cameraRollButton setImage:latestPhoto forState:UIControlStateNormal];
}
}];
}
} failureBlock: ^(NSError *error) {
}];
}

- 4,393
- 2
- 39
- 64
Xamarin.iOS version of accepted answer (how to get last image) including all notices from other answers:
private void ChooseLastTakenPictureImplementation()
{
var library = new ALAssetsLibrary();
// Enumerate just the photos and videos group by using ALAssetsGroupSavedPhotos.
library.Enumerate(ALAssetsGroupType.SavedPhotos, (ALAssetsGroup assetsGroup, ref bool stop) =>
{
if (stop || assetsGroup == null)
{
return;
}
//Xamarin does not support ref parameters in nested lamba expressions
var lambdaStop = false;
//Check that the group has more than one picture
if (assetsGroup.Count > 0)
{
// Within the group enumeration block, filter to enumerate just photos.
assetsGroup.SetAssetsFilter(ALAssetsFilter.AllPhotos);
// Chooses the photo at the last index
assetsGroup.Enumerate(NSEnumerationOptions.Reverse, (ALAsset result, int index, ref bool innerStop) =>
{
// The end of the enumeration is signaled by asset == nil.
if (result != null)
{
var representation = result.DefaultRepresentation;
var latestPhoto = new UIImage(representation.GetImage(), representation.Scale, (UIImageOrientation)representation.Orientation);
// Stop the enumerations
lambdaStop = innerStop = true;
// Do something interesting with the AV asset.
HandleImageAutoPick(latestPhoto);
}
});
stop = lambdaStop;
return;
}
else
{
//Handle this special case where user has no pictures
}
}, error =>
{
// Typically you should handle an error more gracefully than this.
Debug.WriteLine(error.Description);
});
}

- 1
- 1

- 3,102
- 2
- 30
- 52
This is a very cool approach but one of the issues is that you have to be able to instantiate PHPhotoLibrary and the other PHPhoto classes at runtime because otherwise there will be link errors on iOS 7.X.X Just wanted to point that out because I am running into these issues now.
Also I believe you have to weak link in the Photos framework in order for the app to run on both devices with iOS 8.X.X and iOS 7.X.X installed (although I have not tested this out yet.)
ONe of the issues I am running into is how to instantiate the PHPhotoLibrary at runtime. Does anyone have code snippets for that?
Actually for the app that I was working on, I did have to finally write runtime code for instantiating PHPhotoLibrary class and calling PHotos framework methods so the app would run on both iOS 7.x.x and iOS 8.x.x. Someone else may run into the same issues so I provided the code below ->
// PHPhotoLibrary_class will only be non-nil on iOS 8.x.x
Class PHPhotoLibrary_class = NSClassFromString(@"PHPhotoLibrary");
if (PHPhotoLibrary_class) {
/**
*
iOS 8..x. . code that has to be called dynamically at runtime and will not link on iOS 7.x.x ...
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
[PHAssetCollectionChangeRequest creationRequestForAssetCollectionWithTitle:title];
} completionHandler:^(BOOL success, NSError *error) {
if (!success) {
NSLog(@"Error creating album: %@", error);
}
}];
*/
// dynamic runtime code for code chunk listed above
id sharedPhotoLibrary = [PHPhotoLibrary_class performSelector:NSSelectorFromString(@"sharedPhotoLibrary")];
SEL performChanges = NSSelectorFromString(@"performChanges:completionHandler:");
NSMethodSignature *methodSig = [sharedPhotoLibrary methodSignatureForSelector:performChanges];
NSInvocation* inv = [NSInvocation invocationWithMethodSignature:methodSig];
[inv setTarget:sharedPhotoLibrary];
[inv setSelector:performChanges];
void(^firstBlock)() = ^void() {
Class PHAssetCollectionChangeRequest_class = NSClassFromString(@"PHAssetCollectionChangeRequest");
SEL creationRequestForAssetCollectionWithTitle = NSSelectorFromString(@"creationRequestForAssetCollectionWithTitle:");
[PHAssetCollectionChangeRequest_class performSelector:creationRequestForAssetCollectionWithTitle withObject:albumName];
};
void (^secondBlock)(BOOL success, NSError *error) = ^void(BOOL success, NSError *error) {
if (success) {
[assetsLib enumerateGroupsWithTypes:ALAssetsGroupAlbum usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
if (group) {
NSString *name = [group valueForProperty:ALAssetsGroupPropertyName];
if ([albumName isEqualToString:name]) {
groupFound = true;
handler(group, nil);
}
}
} failureBlock:^(NSError *error) {
handler(nil, error);
}];
}
if (error) {
NSLog(@"Error creating album: %@", error);
handler(nil, error);
}
};
// Set the first and second blocks.
[inv setArgument:&firstBlock atIndex:2];
[inv setArgument:&secondBlock atIndex:3];
[inv invoke];
}
else {
// code that always creates an album on iOS 7.x.x but fails
// in certain situations such as if album has been deleted
// previously on iOS 8...x. .
[assetsLib addAssetsGroupAlbumWithName:albumName
resultBlock:^(ALAssetsGroup *group) {
handler(group, nil);
} failureBlock:^(NSError *error) {
NSLog( @"Failed to create album: %@", albumName);
handler(nil, error);
}];
}

- 1,271
- 12
- 20
The following code works with iOS7 and iOS8. It also checks if there is an image in the filter. Before you execute the code you should check the album permission:
// get the latest image from the album
-(void)getLatestPhoto
{
NSLog(@"MMM TGCameraViewController - getLatestPhoto");
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
// Enumerate just the photos and videos group by using ALAssetsGroupSavedPhotos.
[library enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
// Within the group enumeration block, filter to enumerate just photos.
[group setAssetsFilter:[ALAssetsFilter allPhotos]];
// For this example, we're only interested in the last item [group numberOfAssets]-1 = last.
if ([group numberOfAssets] > 0) {
[group enumerateAssetsAtIndexes:[NSIndexSet indexSetWithIndex:[group numberOfAssets]-1]
options:0
usingBlock:^(ALAsset *alAsset, NSUInteger index, BOOL *innerStop) {
// The end of the enumeration is signaled by asset == nil.
if (alAsset) {
ALAssetRepresentation *representation = [alAsset defaultRepresentation];
// Do something interesting with the AV asset.
UIImage *img = [UIImage imageWithCGImage:[representation fullScreenImage]];
// use the photo here ...
// we only need the first (most recent) photo -- stop the enumeration
*innerStop = YES;
}
}];
}
}
failureBlock: ^(NSError *error) {
// Typically you should handle an error more gracefully than this.
NSLog(@"No groups");
}];
}
(This code is a modified version from here.)

- 32,527
- 49
- 210
- 370