Save video to custom album in camera roll - ios

I am able to save an image into a custom album in the iPhone camera roll. For example, the album name could be "Fun", and all of the images I take will go into that folder. I came across a helper class here that works very well to accomplish what I want.
However, the method I am using does not save videos into the custom folder, but the folder is created - it's just empty of any videos that I attempt to save there. I tried debugging it with no progress. That method is here:
-(void)addAssetURL:(NSURL*)assetURL toAlbum:(NSString*)albumName withCompletionBlock:(SaveImageCompletion)completionBlock
{
__block BOOL albumWasFound = NO;
//search all photo albums in the library
[self enumerateGroupsWithTypes:ALAssetsGroupAlbum
usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
//compare the names of the albums
if ([albumName compare: [group valueForProperty:ALAssetsGroupPropertyName]]==NSOrderedSame) {
//target album is found
albumWasFound = YES;
NSData *data = [NSData dataWithContentsOfURL:assetURL];
if ([data length] > 0) {
//get a hold of the photo's asset instance
[self assetForURL: assetURL
resultBlock:^(ALAsset *asset) {
[group addAsset: asset];
//run the completion block
completionBlock(nil);
} failureBlock: completionBlock];
}
//album was found, bail out of the method
return;
}
if (group==nil && albumWasFound==NO) {
//photo albums are over, target album does not exist, thus create it
__weak ALAssetsLibrary* weakSelf = self;
//create new assets album
[self addAssetsGroupAlbumWithName:albumName
resultBlock:^(ALAssetsGroup *group) {
//get the photo's instance
[weakSelf assetForURL: assetURL
resultBlock:^(ALAsset *asset) {
NSLog(#"asset: %#",asset);
//add photo to the newly created album
[group addAsset: asset];
//call the completion block
completionBlock(nil);
} failureBlock: completionBlock];
} failureBlock: completionBlock];
//should be the last iteration anyway, but just in case
return;
}
} failureBlock: completionBlock];
}
Does anyone know the proper way to save a video into a custom album in the photo library?

Try this, it works for me.
-(void)saveVideo:(NSURL *)videoUrl toAlbum:(NSString*)albumName withCompletionBlock: (SaveImageCompletion)completionBlock
{
//write the image data to the assets library (camera roll)
[self writeVideoAtPathToSavedPhotosAlbum:videoUrl completionBlock:^(NSURL* assetURL, NSError* error) {
//error handling
if (error!=nil) {
completionBlock(error);
return;
}
//add the asset to the custom photo album
[self addAssetURL: assetURL
toAlbum:albumName
withCompletionBlock:completionBlock];
}];
}

Related

How to get image from asset library iOS?

I am iOS developer i want to get all images from library, without UIImagepickercontroller,and take first 10 images
Any ideas?
There are lots of example out der which will guide you how to get images from ALAssetLibrary
https://www.cocoacontrols.com/search?q=image+picker.
Below is example to get the latest image from ImagePicker
- (void)latestPhotoWithCompletion:(void (^)(UIImage *photo))completion
{
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]];
// completion
completion(img);
// 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.
}];
}
Usage
__weak __typeof(self)wSelf = self;
[self latestPhotoWithCompletion:^(UIImage *photo) {
UIImageRenderingMode renderingMode = YES ? UIImageRenderingModeAlwaysOriginal : UIImageRenderingModeAlwaysTemplate;
[wSelf.switchCameraBut setImage:[photo imageWithRenderingMode:renderingMode] forState:UIControlStateNormal];
}];

Check if an image is already in gallery and retrieve it

I need to know if an image is already in the iOS gallery. I am developing a messaging client app and, in my chat list, I am able to download images/videos of incoming messages when I tap into the image thumbnail.
The issue is, when I tap in the thumbnail, I download and save that image into the iOS photo gallery, but if I tap again I don't want to download and save it again, I want to retrieve it from the photo gallery.
Resuming, I want to look for the image in the photo gallery and retrieve it.
Here is my code to save the image in my custom photo album using ALAssetsLibrary:
[self writeImageToSavedPhotosAlbum:image.CGImage orientation:(ALAssetOrientation)image.imageOrientation completionBlock:
^(NSURL* assetURL, NSError* error)
{
if (error!=nil) {
completionBlock(error);
return;
}
//add the asset to the custom photo album
[self addAssetURL: assetURL
toAlbum:albumName
withCompletionBlock:completionBlock];
}
];
-(void)addAssetURL:(NSURL*)assetURL toAlbum:(NSString*)albumName withCompletionBlock:(SaveImageCompletion)completionBlock
{
__block BOOL albumWasFound = NO;
[self enumerateGroupsWithTypes:ALAssetsGroupAlbum
usingBlock:
^(ALAssetsGroup *group, BOOL *stop)
{
if ([albumName compare: [group valueForProperty:ALAssetsGroupPropertyName]]==NSOrderedSame) {
albumWasFound = YES;
[self assetForURL: assetURL
resultBlock:
^(ALAsset *asset)
{
//add photo to the target album
[group addAsset: asset];
completionBlock(nil);
} failureBlock: completionBlock
];
return;
}
if (group==nil && albumWasFound==NO) {
//Target album does not exist, create it
__weak ALAssetsLibrary* weakSelf = self;
[self addAssetsGroupAlbumWithName:albumName
resultBlock:
^(ALAssetsGroup *group)
{
[weakSelf assetForURL: assetURL
resultBlock:
^(ALAsset *asset)
{
//add photo to the newly created album
[group addAsset: asset];
completionBlock(nil);
} failureBlock: completionBlock
];
} failureBlock: completionBlock
];
return;
}
} failureBlock: completionBlock
];
}
Maybe with the assetURL I could do it, but I have been searching in Apple's documentation and I didn't see anything.
Thanks!!
You can get Asset URL after the save process using completion block. You can retrieve the saved image using this Asset URL.
Unfortunately, you can't check file exist in Photolibrary. The reason behind is, the system will automatically embed many extra information with the image while you save in to Photolibrary. example like location, time etc.. So the binary content will be different for same images.
So, you have only one solution. Save the images into document directory and do a file exist using algorithms like CRC, MD5 etc..

How to create and save specific photo album using UIActivityViewController in iOS

I want to create custom photo album and save photo to specific album in iOS 7. and I found iOS save photo in an app specific album using ALAssetsLibrary.
But I don't know how to do it using UIActivityViewController.
NSArray* actItems = [NSArray arrayWithObjects: image, nil];
UIActivityViewController *activityView = [[UIActivityViewController alloc]
initWithActivityItems:actItems
applicationActivities:nil];
[activityView setCompletionHandler:^(NSString *activityType, BOOL completed)
{
if ([activityType isEqualToString:UIActivityTypeSaveToCameraRoll])
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(#"#Saved_title", nil)
message:NSLocalizedString(#"#Saved_message2", nil)
delegate:self
cancelButtonTitle:NSLocalizedString(#"OK", nil)
otherButtonTitles: nil];
[alert show];
}
}];
I realize the question is a few months old and that the OP may have moved on, but I needed to do exactly this, but didn't find another solution, and needed to come up with my own.
It's not fool-proof, as if there are multiple copies of the video/image in Camera Roll that you're wanting to copy, it will copy all of them, which may or may not be desired. Also, for my purposes, I'm considering maintaining a persistent list of asset URLs that I've written to the custom album so that I skip that asset in the future, but I haven't yet coded it because, if the user removes the video/image from the custom album, it'd be nearly impossible to update that list without more iteration through asset groups: something I'd like to avoid.
Last disclaimer: I haven't tested this a whole ton yet, but it works for my purposes, so hopefully someone else out there can benefit from it.
I augmented Marin Todorov's ALAssetsLibrary+CustomPhotoAlbum category, which I found here: https://github.com/yusenhan/Smooth-Line-View/tree/master/ALAssetsLibrary%2BCustomPhotoAlbum. Note: there is no license information provided there, so I'm assuming that modifying the source for this category is ok.
Here is my method which sets up and displays the UIActivityViewController:
- (void)showActivitySheet:(NSString *)path {
NSURL *newURL = [NSURL fileURLWithPath:path];
NSArray *itemsToShare = #[newURL];
UIActivityViewController *activityVC = [[UIActivityViewController alloc] initWithActivityItems:itemsToShare applicationActivities:nil];
activityVC.excludedActivityTypes = #[UIActivityTypePrint, UIActivityTypeAssignToContact, UIActivityTypeAddToReadingList];
// Once the OS has finished with whatever the user wants to do with it,
// we'll begin figuring out if we need to save it to the Album, too!
[activityVC setCompletionHandler:^(NSString *activityType, BOOL completed) {
// If the user selected "Save to Camera Roll"
if ([activityType isEqualToString:UIActivityTypeSaveToCameraRoll]) {
ALAssetsLibrary *lib = [[ALAssetsLibrary alloc] init];
NSURL *tempUrl = [NSURL fileURLWithPath:path];
// saveMovie: below is a method I added to the category, but you can use saveImage:,
// which you'll also likely want to add some kind of parameter to so, in the category,
// you know when you only want to copy to the Album instead of the Album AND Camera Roll
[lib saveMovie:tempUrl toAlbum:#"CUSTOM ALBUM NAME"
withCompletionBlock:^(NSURL *url, NSError *error) {
if (error) {
NSLog(#"Error writing movie to custom album: %#", error.debugDescription);
}
}
onlyWriteToCustomAlbum:YES];
}
}];
[self presentViewController:activityVC animated:YES completion:nil];
}
Here is code you can add to the saveImage: method in the ALAssetsLibrary+CustomPhotoAlbum category so you can save it to your custom Album, too! You could execute this portion only on the YES case of the BOOL which you'll want to add to the category.
NSData *dataToCompareTo = [NSData dataWithContentsOfURL:url]; // This data represents the image/movie which you want to save into the custom album. The one you handed to the UIActivityViewController.
// Note use of self, as this is a category on ALAssetsLibrary; also, this
// assumes that you've already written the photo to the Camera Roll, which
// should be automatically handled by the OS, if the user hit the "Save" button
// on the UIActivityViewController.
// Enumerate through Camera Roll/Saved Photos group.
[self enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos
usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
// Enumerate the assets in each group.
[group enumerateAssetsUsingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {
ALAssetRepresentation *rep = [result defaultRepresentation];
// If the asset isn't the same size as the source image/movie
// it shouldn't be the same. Check the size first, since it
// is a less costly operation than byte checking the data itself.
if ([rep size] == [dataToCompareTo length]) {
Byte *buffer = malloc([NSNumber numberWithLongLong:rep.size].unsignedLongValue);
NSUInteger buffered = [rep getBytes:buffer fromOffset:0.0 length:[NSNumber numberWithLongLong:rep.size].unsignedLongValue error:nil];
NSData *data = [NSData dataWithBytesNoCopy:buffer length:buffered freeWhenDone:YES];
// If the buffer has more than the null-termination char, free it! I'm doing this in case the above call to dataWithBytesNoCopy fails to free() the buffer.
if (sizeof(buffer) > 4) {
free(buffer);
}
// Ensure they are the same by comparing the NSData instances.
if ([data isEqualToData:dataToCompareTo]) {
NSLog(#"they are the same!!");
[self addAssetURL:[rep url]
toAlbum:albumName
withMovieCompletionBlock:completionBlock];
} else {
NSLog(#"they are different");
}
}
}];
} failureBlock:^(NSError *error) {
NSLog(#"Failed to write to custom album!");
}];
Hope it helps! TLDR? Thought so! ;-)

ALAssetLibrary for audio

I am trying to load all the files(audio, video and images) in my application using ALAssetLibrary by this piece of code:
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library enumerateGroupsWithTypes:ALAssetsGroupAll usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
// Within the group enumeration block, filter to enumerate everything.
[group setAssetsFilter:[ALAssetsFilter allAssets]];
// Accessing all the assets.
for (int i = 0;i<[group numberOfAssets] ; i++) {
[group enumerateAssetsAtIndexes:[NSIndexSet indexSetWithIndex:i] 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];
NSURL *url = [representation url];
NSLog(#"%#",url);
AVAsset *avAsset = [AVURLAsset URLAssetWithURL:url options:nil];
//Doing some stuff with assets
}
}];
}
}
failureBlock: ^(NSError *error) {
NSLog(#"No groups");
}];
However, this only loads video and images, not the audio files. How can I do that?
Regarding Apple.Developer ALAsset documentaion ,An ALAsset object represents a photo or a video managed by the Photo application.
Assets can have multiple representations, for example a photo which was captured in RAW and JPG. Different representations of the same asset may have different dimensions.
So i think ,ALAssetsLibrary will not retrieve what you need :)

Get last image from Photos.app?

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?
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];
});
}];
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.
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];
});
}];
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)
}
}
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
}
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.
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) {
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.
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
}
}
}
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) {
}];
}
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);
});
}
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);
}];
}
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.)

Resources