i have created an album using this code in AppDelegate methode
NSString *albumName=#"999Videos";
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library addAssetsGroupAlbumWithName:albumName
resultBlock:^(ALAssetsGroup *group) {
NSLog(#"added album:%#", albumName);
}
failureBlock:^(NSError *error) {
NSLog(#"error adding album");
}];
Now i want to save the recorded videos to this 999Videos album created.Not to the photosAlbum which i have done like this.
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
if ([library videoAtPathIsCompatibleWithSavedPhotosAlbum:outputFileURL])
{
[library writeVideoAtPathToSavedPhotosAlbum:outputFileURL
completionBlock:^(NSURL *assetURL, NSError *error)
The videos are saving but not in the 999Videos album.Could someone please tell me how can i save the videos to my custom album?
After tearing my hair out over this finally i found the solution.Here is my code.
NSString *albumName=#"999 Videos";
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library addAssetsGroupAlbumWithName:albumName
resultBlock:^(ALAssetsGroup *group) {
NSLog(#"added album:%#", albumName);
}
failureBlock:^(NSError *error) {
NSLog(#"error adding album");
}];
__block ALAssetsGroup* groupToAddTo;
[library enumerateGroupsWithTypes:ALAssetsGroupAlbum
usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
if ([[group valueForProperty:ALAssetsGroupPropertyName] isEqualToString:albumName]) {
NSLog(#"found album %#", albumName);
groupToAddTo = group;
}
}
failureBlock:^(NSError* error) {
NSLog(#"failed to enumerate albums:\nError: %#", [error localizedDescription]);
}];
[library assetForURL:assetURL
resultBlock:^(ALAsset *asset) {
// assign the photo to the album
[groupToAddTo addAsset:asset];
NSLog(#"Added %# to %#", [[asset defaultRepresentation] filename], albumName);
}
failureBlock:^(NSError* error) {
NSLog(#"failed to retrieve image asset:\nError: %# ", [error localizedDescription]);
}];
Related
I'm having a weird issue trying to save an image to my SavedPhotosAlbum using the ALAssetsLibrary, particularly when I'm using the writeImageToSavedPhotosAlbum method.
Basically my issue is that the NSURL is returning null within my completion block... there is no Error, but the URL is null.
Here's the method :
UIImage *imageToSave = [libraryPicture imageFromCurrentlyProcessedOutput];
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library writeImageToSavedPhotosAlbum:[imageToSave CGImage] orientation:ALAssetOrientationUp completionBlock:^(NSURL *assetURL, NSError *error) {
NSLog(#"AssetURL: %#", assetURL);
if(!error) {
NSLog(#"\t ! Error");
NSLog(#"\t Error: %#", [error localizedDescription]);
NSLog(#"\t Error code %d", [error code]);
}
if(error != nil) {
NSLog(#"\t ERROR != NIL");
NSLog(#"\t Error - Image Failed To Save With Error: %#", [error localizedDescription]);
NSLog(#"\t Error code %d", [error code]);
}
if(error == nil) {
NSLog(#"\t ERROR == NIL");
}
}];
I use a pretty similar method just a few lines away (in another switch case) that works, and it prints the URL within an almost identical completion block (though the called method differs):
[stillCamera capturePhotoAsJPEGProcessedUpToFilter:selectedFilter withCompletionHandler:^(NSData *processedJPEG, NSError *error) {
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library writeImageDataToSavedPhotosAlbum:processedJPEG metadata:stillCamera.currentCaptureMetadata completionBlock:^(NSURL *assetURL, NSError *error2) {
if (error2 != nil) {
NSLog(#"ERROR: the image failed to be written");
}
else {
NSLog(#"PHOTO SAVED FROM STILL CAMERA - assetURL: %#", assetURL);
}
}];
}];
If you really are seeing the behaviour you state, this would appear to be the Asset Library breaking its API contract. You should file a radar with Apple with an example project that demonstrates this, and perhaps also file a DTS incident for a speedier response.
I have url for image picked using ELCImagePickerController. I stored the url for future reference.
I get that URL using:
[dict valueForKey:UIImagePickerControllerReferenceURL];
Now the problem arises when after some time user deleted that particular image from photo library
and I am going to access that image using URL.
My app does not get crashed.
I have tried using NSUrl method
[imagePath checkResourceIsReachableAndReturnError:&err]
as well i tried something like:
-(BOOL)findImage:(NSURL*)path
{
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
__block BOOL flag=YES;
ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *myasset)
{
ALAssetRepresentation *rep = [myasset defaultRepresentation];
CGImageRef iref = [rep fullScreenImage];
if (iref) {
flag=YES;
dispatch_group_leave(group);
}
};
ALAssetsLibraryAccessFailureBlock failureblock = ^(NSError *myerror)
{
NSLog(#"cant get image - %#",[myerror localizedDescription]);
flag=NO;
};
ALAssetsLibrary* assetslibrary = [[ALAssetsLibrary alloc] init];
[assetslibrary assetForURL:path resultBlock:resultblock failureBlock:failureblock];
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_release(group);
[assetslibrary release];
return flag;
}
Sample url :
assets-library://asset/asset.JPG?id=E862927E-E646-448A-9EB6-A7D48668B3DC&ext=JPG
But no success.
How to know that image present at particular URL.
If any one can help me out on this will be appreciable.
Thanks in advance.
For this case you need to check ALAssetRepresentation *rep = [myasset defaultRepresentation] to nil.
if(rep != nil){
//write your code..
}
Solved the problem with change in the findImage method
-(BOOL)findImage:(NSURL*)path
{
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
__block BOOL flag=YES;
ALAssetsLibrary* assetslibrary = [[ALAssetsLibrary alloc] init];
[assetslibrary assetForURL:path resultBlock:^(ALAsset *asset) {
if (asset==nil)
{
flag=NO;
}
else
{
flag=YES;
}
dispatch_group_leave(group);
} failureBlock:^(NSError *error){
NSLog(#"operation was not successfull!");
dispatch_group_leave(group);
}];
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_release(group);
[assetslibrary release];
return flag;
}
I've got an array containing ALAsset urls (not full ALAsset objects)
So each time I start my application I want to check my array to see if it's still up to date...
So I tried
NSData *assetData = [[NSData alloc] initWithContentsOfFile:#"assets-library://asset/asset.PNG?id=1000000001&ext=PNG"];
but assetData is allways nil
thx for the help
Use assetForURL:resultBlock:failureBlock: method of ALAssetsLibrary instead to get the asset from its URL.
// Create assets library
ALAssetsLibrary *library = [[[ALAssetsLibrary alloc] init] autorelease];
// Try to load asset at mediaURL
[library assetForURL:mediaURL resultBlock:^(ALAsset *asset) {
// If asset exists
if (asset) {
// Type your code here for successful
} else {
// Type your code here for not existing asset
}
} failureBlock:^(NSError *error) {
// Type your code here for failure (when user doesn't allow location in your app)
}];
Having assets path you can use this function to check if image exist:
-(BOOL) imageExistAtPath:(NSString *)assetsPath
{
__block BOOL imageExist = NO;
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library assetForURL:[NSURL URLWithString:assetsPath] resultBlock:^(ALAsset *asset) {
if (asset) {
imageExist = YES;
}
} failureBlock:^(NSError *error) {
NSLog(#"Error %#", error);
}];
return imageExist;
}
Remember that checking if image exist is checking asynchronyus.
If you want to wait until new thread finish his life call function "imageExistAtPath" in main thread:
dispatch_async(dispatch_get_main_queue(), ^{
[self imageExistAtPath:assetPath];
});
Or you can use semaphores, but this is not very nice solution:
-(BOOL) imageExistAtPath:(NSString *)assetsPath
{
__block BOOL imageExist = YES;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_async(queue, ^{
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library assetForURL:[NSURL URLWithString:assetsPath] resultBlock:^(ALAsset *asset) {
if (asset) {
dispatch_semaphore_signal(semaphore);
} else {
imageExist = NO;
dispatch_semaphore_signal(semaphore);
}
} failureBlock:^(NSError *error) {
NSLog(#"Error %#", error);
}];
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
return imageExist;
}
For iOS 8 or later, there is a synchronous method to check if an ALAsset exists.
#import Photos;
if ([PHAsset fetchAssetsWithALAssetURLs:#[assetURL] options:nil].count) {
// exist
}
Swift:
import Photos
if PHAsset.fetchAssetsWithALAssetURLs([assetURL], options: nil).count > 0 {
// exist
}
Swift 3:
import Photos
if PHAsset.fetchAssets(withALAssetURLs: [assetURL], options: nil).count > 0 {
// exist
}
Use this method to check if file exists
NSURL *yourFile = [[self applicationDocumentsDirectory]URLByAppendingPathComponent:#"YourFileHere.txt"];
if ([[NSFileManager defaultManager]fileExistsAtPath:storeFile.path
isDirectory:NO]) {
NSLog(#"The file DOES exist");
} else {
NSLog(#"The file does NOT exist");
}
I would like to wait this code to be executed before to continue but as these blocks are called assynchronously I don't know how to do???
NSURL *asseturl;
NSMutableArray *tmpListAsset = [[NSMutableArray alloc] init];
ALAssetsLibrary *library = [[[ALAssetsLibrary alloc] init] autorelease];
NSMutableArray *objectsToRemove = [[NSMutableArray alloc] init];
for (NSDictionary *dico in assetsList) {
asseturl = [NSURL URLWithString:[dico objectForKey:#"assetUrl"]];
NSLog(#"asset url %#", asseturl);
// Try to load asset at mediaURL
[library assetForURL:asseturl resultBlock:^(ALAsset *asset) {
// If asset doesn't exists
if (!asset){
[objectsToRemove addObject:dico];
}else{
[tmpListAsset addObject:[asseturl absoluteString]];
NSLog(#"tmpListAsset : %#", tmpListAsset);
}
} failureBlock:^(NSError *error) {
// Type your code here for failure (when user doesn't allow location in your app)
}];
}
GCD semaphore approach:
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
for (NSURL *url in self.assetUrls) {
dispatch_async(queue, ^{
[library assetForURL:url resultBlock:^(ALAsset *asset) {
[self.assets addObject:asset];
dispatch_semaphore_signal(sema);
} failureBlock:^(NSError *error) {
dispatch_semaphore_signal(sema);
}];
});
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
}
dispatch_release(sema);
/* Check out ALAssets */
NSLog(#"%#", self.assets);
The solution is here
http://omegadelta.net/2011/05/10/how-to-wait-for-ios-methods-with-completion-blocks-to-finish/
Note that assetForURL:resultBlock:failureBlock: will stuck if the main thread is waiting without RunLoop running. This is alternative ( cleaner :-) ) solution:
#import <libkern/OSAtomic.h>
...
ALAssetsLibrary *library;
NSMutableArray *assets;
...
__block int32_t counter = 0;
for (NSURL *url in urls) {
OSAtomicIncrement32(&counter);
[library assetForURL:url resultBlock:^(ALAsset *asset) {
if (asset)
[assets addObject:asset];
OSAtomicDecrement32(&counter);
} failureBlock:^(NSError *error) {
OSAtomicDecrement32(&counter);
}];
}
while (counter > 0) {
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01]];
}
The easiest thing to do is to move your code to inside (at the end of) the resultBlock or the failureBlock. That way, your code will run in the correct order, and you will also retain asynchronous behaviour.
This is an easy way to do it. Maybe not as elegant as using GCD but it should get the job done ... This will make your method blocking instead of non-blocking.
__block BOOL isFinished = NO;
NSURL *asseturl;
NSMutableArray *tmpListAsset = [[NSMutableArray alloc] init];
ALAssetsLibrary *library = [[[ALAssetsLibrary alloc] init];
NSMutableArray *objectsToRemove = [[NSMutableArray alloc] init];
for (NSDictionary *dico in assetsList) {
asseturl = [NSURL URLWithString:[dico objectForKey:#"assetUrl"]];
NSLog(#"asset url %#", asseturl);
// Try to load asset at mediaURL
[library assetForURL:asseturl resultBlock:^(ALAsset *asset) {
// If asset doesn't exists
if (!asset){
[objectsToRemove addObject:dico];
}else{
[tmpListAsset addObject:[asseturl absoluteString]];
NSLog(#"tmpListAsset : %#", tmpListAsset);
}
if (objectsToRemove.count + tmpListAsset.count == assetsList.count) {
isFinished = YES;
}
} failureBlock:^(NSError *error) {
// Type your code here for failure (when user doesn't allow location in your app)
isFinished = YES;
}];
}
while (!isFinished) {
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01f]];
}
I would like to know how to retrieve video format using AssetLibrary and display on uilabel? i read on forum to use ALAssetPropertyRepresentations but im just lost. some help please?
I'm not sure what you mean by "format", but this code will display the video's UTI (basically, file type) and any metadata.
ALAssetsGroupEnumerationResultsBlock assetEnumerator = ^(ALAsset *asset, NSUInteger index, BOOL *stop) {
if(asset != nil) {
ALAssetRepresentation* representation = [asset defaultRepresentation];
NSLog(#"UTI = %#", [representation UTI]);
NSLog(#"Metadata = %#", [representation metadata]);
}
};
ALAssetsLibraryGroupsEnumerationResultsBlock assetGroupEnumerator = ^(ALAssetsGroup *group, BOOL *stop) {
if(group != nil) {
[group setAssetsFilter:[ALAssetsFilter allVideos]];
[group enumerateAssetsUsingBlock:assetEnumerator];
}
};
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library enumerateGroupsWithTypes:ALAssetsGroupAll
usingBlock:assetGroupEnumerator
failureBlock:^(NSError *error) {
NSLog(#"A problem occured. %#", error);
}];
[library release];
(Code modified from here.)
If you already have your asset, the code to update the label looks like this:
myLabel.text = [[myAsset defaultRepresentation] UTI];