I'm adding images from the Asset Library using this code. I'm 99% sure (from Instruments) that the iref isn't being released and a new CG Image is being retained in memory every time I call this method. However, if I add: CGImageRelease(iref) the app crashes.
-(void)setBackgroundWithPhotoURL: (NSURL*)photoURL
{
ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *myasset)
{
#autoreleasepool {
ALAssetRepresentation *rep = [myasset defaultRepresentation];
CGImageRef iref = [rep fullResolutionImage];
UIImage *selectedBackground = [UIImage imageWithCGImage:iref scale:[rep scale] orientation:(UIImageOrientation)[rep orientation]];
if (selectedBackground) {
[self setBackgroundWithImage:selectedBackground orColor:nil];
}
}
};
ALAssetsLibraryAccessFailureBlock failureblock = ^(NSError *myerror)
{
NSLog(#"Can't get image - %#",[myerror localizedDescription]);
};
ALAssetsLibrary* assetslibrary = [[ALAssetsLibrary alloc] init];
[assetslibrary assetForURL:photoURL resultBlock:resultblock failureBlock:failureblock];
}
Related
I'm using UImagePickerController to take a photo from the camera on iOS9. Then I found that using writeImageToSavedPhotosAlbum to save the image is different from saving the image using UIImageJPEGRepresentation at the file size. Code as follow:
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingImage:(UIImage *)image editingInfo:(nullable NSDictionary<NSString *,id> *)info {
if( [picker sourceType] == UIImagePickerControllerSourceTypeCamera ) {
[picker dismissViewControllerAnimated:YES completion:nil];
UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library writeImageToSavedPhotosAlbum:image.CGImage orientation:(ALAssetOrientation)image.imageOrientation completionBlock:^(NSURL *assetURL, NSError *error ) {
// NSLog(#"url: %#", assetURL.absoluteString);
NSData *data1 = UIImageJPEGRepresentation(image, 1.0);
NSString *path = [NSString stringWithFormat:#"%#%#", NSTemporaryDirectory(), #"/tmp.jpg"];
[data1 writeToFile:path atomically:YES];
[library assetForURL:assetURL resultBlock:^(ALAsset *asset) {
ALAssetRepresentation *rep = [asset defaultRepresentation];
// CGImageRef iref = [rep fullResolutionImage];
NSLog(#"size1: %lu, size2: %lld", data1.length, rep.size);
} failureBlock:^(NSError *error) {
}];
}];
}
}
it prints:
2016-09-08 10:10:56.690658 TestPhotoes[6096:2345233] size1: 5549568, size2: 2288036
You see, the image file saved by UIImageJPEGRepresentation is way much bigger than by writeImageToSavedPhotosAlbum. I was wondering what the writeImageToSavedPhotosAlbum did? Does it compress the origin image? How can I save image using UIImageJPEGRepresentation to achieve the same effect, both image file size, and the image quality?
I have ViewController1 and ViewController2.
ViewController1 is UICollectionView and it use ASAssetLibrary to load thumbnail into UICollectionViewCell:
NSMutableArray *collector = [[NSMutableArray alloc] initWithCapacity:0];
ALAssetsLibrary *al = [TemplateEditBarController defaultAssetsLibrary];
[al enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos
usingBlock:^(ALAssetsGroup *group, BOOL *stop)
{
[group enumerateAssetsUsingBlock:^(ALAsset *asset, NSUInteger index, BOOL *stop)
{
if (asset) {
[collector addObject:asset];
}
}];
assetsarray = collector;//<--assetsarray is local.
}
failureBlock:^(NSError *error) { NSLog(#"fail!");}
];
When cell selected, I want to pass either NSString or NSURL to ViewController2 so that it will call and display the full resolution image. But when NSLog the url:
ALAsset* asset = backgroundsarray[row];
ALAssetRepresentation *representation = [asset defaultRepresentation];
NSURL *url = [representation url];
newbg = [url absoluteString];//<-NSLog this.
I get:
assets-library://asset/asset.JPG?id=6E5438ED-9A8C-4ED0-9DEA-AB2D8F8A9360&ext=JPG
I tried change to [url path] I get:
asset.JPG
How can I get the actual photo url so that I can convert to NSString to pass to ViewController2?
NSURL* aURL = [NSURL URLWithString:imagePath];
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library assetForURL:aURL resultBlock:^(ALAsset *asset)
{
UIImage *image = [UIImage imageWithCGImage:[[asset defaultRepresentation] fullScreenImage] scale:0.5 orientation:UIImageOrientationUp];
imageView.image=image;
}
failureBlock:^(NSError *error)
{
// error handling
NSLog(#"failure-----");
}];
My solution here:
ALAssetsLibrary *assetslibrary = [[ALAssetsLibrary alloc] init];
[assetslibrary assetForURL:assetURL resultBlock:^(ALAsset *asset) {
if (asset) {
image = [UIImage imageWithCGImage:[asset aspectRatioThumbnail]];
}
}
Apparently there isn't any direct solution other than passing ALAsset. This is what I do:
In ViewController1, which is UICollectionViewController, I have this function:
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
//NSLog(#"selected row: %ld",(long)indexPath.row);
long row = [indexPath row];
NSString* newbg = [[NSString alloc]init];
if ([bartype isEqualToString:#"photolibrary"]) {
ALAsset* asset = backgroundsarray[row];
[_delegate changeBackgroundWithAsset:asset];
}
Then I have a function in ViewController2 to process ALAsset:
-(void)changeBackgroundWithAsset:(ALAsset*)b{
ALAssetRepresentation *rep = [b defaultRepresentation];
CGImageRef iref = [rep fullResolutionImage];
UIImage *bimage;
bimage = [UIImage imageWithCGImage:iref];
UIImageView* bgview = [[UIImageView alloc]init];
bgview = curbgview;
bgview.image = bimage;
}
In this code snippet the flow of program goes
out of for loop first then only goes inside the block
resultBlock:^(ALAsset *asset).
The code prints the NSLog at the bottom first then executes the block inside the loop. What's happening here?
ALAssetsLibrary *lib=[ALAssetsLibrary new];
_sizeOfSelectedImage=0;
for (int i=0; i<assets.count; i++) {
ALAsset *asset=assets[i];
FileOP *fileMgr=[[FileOP alloc]init];
NSString *baseDir=[fileMgr GetDocumentDirectory];
//STORING FILE INTO LOCAL
[lib assetForURL:asset.defaultRepresentation.url
resultBlock:^(ALAsset *asset){
ALAssetRepresentation *repr = [asset defaultRepresentation];
CGImageRef cgImg = [repr fullResolutionImage];
NSString *fname = repr.filename;
UIImage *img = [UIImage imageWithCGImage:cgImg];
NSData *data = UIImagePNGRepresentation(img);
[data writeToFile:[baseDir stringByAppendingPathComponent:fname]
atomically:YES];
//FOR LOCAL URL OF THE IMAGE
//NSString *imageURL = [baseDir stringByAppendingPathComponent:fname];
//UIImage *myImg =[UIImage imageWithContentsOfFile:imageURL];
//NSLog(#"%# URL OF IMAGE ",imageURL);
NSLog(#"Image %d has %d size",i,data.length);
_sizeOfSelectedImage +=data.length;
NSLog(#"%d is the size",_sizeOfSelectedImage);
}
failureBlock:^(NSError *error){
}];
}
NSLog(#"COPIED %lu FILE INTO LOCAL MEMORY AND TOTAL SIZE COPIED IS %d ",(unsigned long)assets.count,_sizeOfSelectedImage);
Method assetForURL:resultBlock:failureBlock: will execute the load of asset asynchronously. That's why we first have the bottom NSLog executed and then in Block. If you want it to be executed synchronously, do it like this:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0) ^{
ALAssetsLibrary *lib=[ALAssetsLibrary new];
_sizeOfSelectedImage=0;
dispatch_group_t group = dispatch_group_create();
for (int i=0;i<assets.count;i++) {
ALAsset *asset=assets[i];
FileOP *fileMgr=[[FileOP alloc]init];
NSString *baseDir=[fileMgr GetDocumentDirectory];
//STORING FILE INTO LOCAL
dispatch_group_enter(group);
[lib assetForURL:asset.defaultRepresentation.url
resultBlock:^(ALAsset *asset){
ALAssetRepresentation *repr = [asset defaultRepresentation];
CGImageRef cgImg = [repr fullResolutionImage];
NSString *fname = repr.filename;
UIImage *img = [UIImage imageWithCGImage:cgImg];
NSData *data = UIImagePNGRepresentation(img);
[data writeToFile:[baseDir stringByAppendingPathComponent:fname]
atomically:YES];
//FOR LOCAL URL OF THE IMAGE
//NSString *imageURL = [baseDir stringByAppendingPathComponent:fname];
//UIImage *myImg =[UIImage imageWithContentsOfFile:imageURL];
//NSLog(#"%# URL OF IMAGE ",imageURL);
NSLog(#"Image %d has %d size",i,data.length);
_sizeOfSelectedImage +=data.length;
NSLog(#"%d is the size",_sizeOfSelectedImage);
dispatch_group_leave(group);
}
failureBlock:^(NSError *error){
dispatch_group_leave(group);
}];
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(#"COPIED %lu FILE INTO LOCAL MEMORY AND TOTAL SIZE COPIED IS %d ",(unsigned long)assets.count,_sizeOfSelectedImage);
dispatch_async(dispatch_get_main_queue(), ^{
// Do your call back on main thread here
});
});
Edit1: Enhanced answer from Ken
ALAssetsLibrary *lib=[ALAssetsLibrary new];
_sizeOfSelectedImage=0;
dispatch_group_t group = dispatch_group_create();
for (int i=0;i<assets.count;i++) {
ALAsset *asset=assets[i];
FileOP *fileMgr=[[FileOP alloc]init];
NSString *baseDir=[fileMgr GetDocumentDirectory];
//STORING FILE INTO LOCAL
dispatch_group_enter(group);
[lib assetForURL:asset.defaultRepresentation.url
resultBlock:^(ALAsset *asset){
ALAssetRepresentation *repr = [asset defaultRepresentation];
CGImageRef cgImg = [repr fullResolutionImage];
NSString *fname = repr.filename;
UIImage *img = [UIImage imageWithCGImage:cgImg];
NSData *data = UIImagePNGRepresentation(img);
[data writeToFile:[baseDir stringByAppendingPathComponent:fname]
atomically:YES];
//FOR LOCAL URL OF THE IMAGE
//NSString *imageURL = [baseDir stringByAppendingPathComponent:fname];
//UIImage *myImg =[UIImage imageWithContentsOfFile:imageURL];
//NSLog(#"%# URL OF IMAGE ",imageURL);
NSLog(#"Image %d has %d size",i,data.length);
_sizeOfSelectedImage +=data.length;
NSLog(#"%d is the size",_sizeOfSelectedImage);
dispatch_group_leave(group);
}
failureBlock:^(NSError *error){
dispatch_group_leave(group);
}];
}
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// Do your call back on main thread here
NSLog(#"COPIED %lu FILE INTO LOCAL MEMORY AND TOTAL SIZE COPIED IS %d ",(unsigned long)assets.count,_sizeOfSelectedImage);
// Your code here
});
I have some images in my Photo Album. I have already retrieved the asset urls of all of them.. I want to get a particular image using the asset url... below code gave me asset url..
ALAssetRepresentation *defaultRepresentation = [asset defaultRepresentation];
NSString *uti = [defaultRepresentation UTI];
NSURL *URL = [[asset valueForProperty:ALAssetPropertyURLs] valueForKey:uti];
Now using this url, how can I get the image(UIImage)?
Get Url by this,
NSURL *url= (NSURL*) [[asset valueForProperty:ALAssetPropertyURLs] valueForKey:[[[asset valueForProperty:ALAssetPropertyURLs] allKeys] objectAtIndex:0]];
And get image from url like below.
ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *myasset)
{
ALAssetRepresentation *rep = [myasset defaultRepresentation];
#autoreleasepool {
CGImageRef iref = [rep fullScreenImage];
if (iref) {
UIImage *image = [UIImage imageWithCGImage:iref];
self.loadedImage = image;
dispatch_async(dispatch_get_main_queue(), ^{
//UIMethod trigger...
});
iref = nil;
}
}
};
ALAssetsLibraryAccessFailureBlock failureblock = ^(NSError *myerror)
{
NSLog(#"Can't get image - %#",[myerror localizedDescription]);
};
ALAssetsLibrary* assetslibrary = [[ALAssetsLibrary alloc] init];
[assetslibrary assetForURL:yourUrl
resultBlock:resultblock
failureBlock:failureblock];
UIImage *img = [UIImage imageWithCGImage:[[myAsset defaultRepresentation] fullResolutionImage]
Hope this helps
I need to save path of the image picked from server and later access the path to show that image on user click by database retrival.
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
NSString *urlPath = [info objectForKey:#"UIImagePickerControllerReferenceURL"]absoluteString];
}
and saving it to database.
It saved as assets-library://asset/asset.JPG?id=E1136225-97DE-4BF4-A864-67E33D60737A&ext=JPG
then I want to import to Imageview
UIImageView *iv = [[UIImageView alloc]init];
iv.image = [UIimage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:imagepath]]];
But it is not working
Try this:
typedef void (^ALAssetsLibraryAssetForURLResultBlock)(ALAsset *asset);
typedef void (^ALAssetsLibraryAccessFailureBlock)(NSError *error);
ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *myasset){
ALAssetRepresentation *rep = [myasset defaultRepresentation];
CGImageRef iref = [rep fullResolutionImage];
if (iref){
UIImage *myImage = [UIImage imageWithCGImage:iref scale:[rep scale] orientation:(UIImageOrientation)[rep orientation]];
[fileImage addObject:myImage];
}
};
ALAssetsLibraryAccessFailureBlock failureblock = ^(NSError *myerror){
//failed to get image.
};
ALAssetsLibrary* assetslibrary = [[[ALAssetsLibrary alloc] init] autorelease];
[assetslibrary assetForURL:[filePath objectAtIndex:0] resultBlock:resultblock failureBlock:failureblock];
Note: Make sure your [filePath objectAtIndex:0] will be a NSUrl object. Else convert it to NSUrl
Example:
ALAssetsLibrary* assetslibrary = [[[ALAssetsLibrary alloc] init] autorelease];
NSURL myAssetUrl = [NSURL URLWithString:[filePath objectAtIndex:0]];
assetslibrary assetForURL:myAssetUrl resultBlock:resultblock failureBlock:failureblock];
Use assetForURL:resultBlock:failureBlock: method of ALAssetsLibrary class to retrieve image by URL.
There is more info: http://developer.apple.com/library/ios/#documentation/AssetsLibrary/Reference/ALAssetsLibrary_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40009722
UPD:
ALAssetsLibrary *lib = [[ALAssetsLibrary alloc] init];
[lib assetForURL: url resultBlock: ^(ALAsset *asset) {
ALAssetRepresentation *r = [asset defaultRepresentation];
self.imgView.image = [UIImage imageWithCGImage: r.fullResolutionImage];
}
failureBlock: nil];
Where is url is your cached URL