Create an array of UIImages from camera roll - objective-c

I would like to get all of the images from the camera roll and create an array of UIImages from them.
I have been trying to figure out how to do this for about a day now and I've gotten nowhere. I can't seem to figure out how to retrieve only items from the Camera Roll. It appears that all of the samples that I've seen all enumerate over all of the photo albums. I might be wrong about that though.
Any help would be appreciated. Thanks!

Have you tried ALAssetsLibrary? like this:
assets = [[NSMutableArray array] init]; // Prepare array to have retrieved images by Assets Library.
void (^assetEnumerator)(ALAsset *, NSUInteger, BOOL *) = ^(ALAsset *asset, NSUInteger index, BOOL *stop) {
if(asset != NULL) {
[assets addObject:asset];
dispatch_async(dispatch_get_main_queue(), ^{
[self insertArray];
});
}
};
void (^assetGroupEnumerator)(ALAssetsGroup *, BOOL *) = ^(ALAssetsGroup *group, BOOL *stop) {
if(group != nil) {
[group enumerateAssetsUsingBlock:assetEnumerator];
}
};
// Create instance of the Assets Library.
library = [[ALAssetsLibrary alloc] init];
[library enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos // Retrieve the images saved in the Camera roll.
usingBlock:assetGroupEnumerator
failureBlock: ^(NSError *error) {
NSLog(#"Failed.");
}];
that'll nab them. then do this to render them (this is wicked hacky. you'll want to import them as needed and not all at once like this, or you'll run into memory issues and crash)
-(void) insertArray {
int i = assetCount++;
if (i>20) {
return;
}
ALAssetRepresentation *rep = [[assets objectAtIndex:i] defaultRepresentation];
CGImageRef iref = [rep fullResolutionImage];
CGSize cSize = CGSizeMake(75,75);
UIImage *largeimage = [UIImage imageWithCGImage:iref];
UIImage *resizedimage = (UIImage *)[largeimage resizedImage:cSize interpolationQuality:kCGInterpolationHigh];
UIImageView *newView = [[UIImageView alloc] initWithImage:resizedimage];
if((i>0)&&(i%4 == 0)){
rowCount++;
}
colCount = i%4;
newView.frame = CGRectMake(4+(colCount*(75+4)), 4+(rowCount*(75+4)), 75, 75);
[sv addSubview:newView];
[sv setContentSize:CGSizeMake(320, 85+(rowCount*(75+4)))];
NSLog(#"sv frame size is %# and i is %i", NSStringFromCGRect(sv.frame), i);
}

Related

Image in UITableView using too much memory

I created a class for download images from URLs for UITableViewCells (in this project I cannot use SDWebImageView or other codes from internet) but it looks like it's using a lot of memory and my tableview is not loading so fast. Can anybody point what is the problem?
Code:
//MyHelper class
+(NSString *)pathForImage:(NSString *)urlImageString{
if ([urlImageString class] == [NSNull class] || [urlImageString isEqualToString:#"<null>"] || [urlImageString isEqualToString:#""]) {
return #"";
}
NSArray *pathsInString = [urlImageString componentsSeparatedByString:#"/"];
NSString *eventCodeString = [pathsInString objectAtIndex:[pathsInString count] - 2];
NSString *imageNameString = [pathsInString lastObject];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cachePath = [paths objectAtIndex:0];
cachePath = [MyHelper validateString:[cachePath stringByAppendingString:eventCodeString]];
[cachePath stringByAppendingString:#"/"];
return [cachePath stringByAppendingString:imageNameString];
}
+(BOOL)imageExistsForURL:(NSString *)urlString{
if (!([urlString class] == [NSNull class]))
{
NSString *filePath = [MyHelper pathForImage:urlString];
NSFileManager *fileManager = [NSFileManager defaultManager];
return [fileManager fileExistsAtPath:filePath];
}
return false;
}
+(void)setAsyncImage:(UIImageView *)imageView forDownloadImage:(NSString *)urlString{
CGRect activityFrame = CGRectMake(0, 0, 60, 60);
UIActivityIndicatorView *activity = [[UIActivityIndicatorView alloc] initWithFrame:activityFrame];
activity.layer.cornerRadius = activity.frame.size.width / 2;
activity.clipsToBounds = YES;
activity.activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray;
[imageView addSubview:activity];
[activity startAnimating];
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(concurrentQueue, ^{
NSData *image;
if ([urlString class] == [NSNull class]) {
image = nil;
} else {
image = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:urlString]];
}
dispatch_async(dispatch_get_main_queue(), ^{
[activity stopAnimating];
[activity removeFromSuperview];
if (image)
{
[UIView animateWithDuration:0.9 animations:^{
imageView.alpha = 0;
imageView.image = [UIImage imageWithData:image];
imageView.alpha = 1;
}];
NSString *filePath = [MyHelper pathForImage:urlString];
NSError *error;
[image writeToFile:filePath options:NSDataWritingAtomic error:&error];
}
else
{
imageView.image = [UIImage imageNamed:#"icn_male.png"];
}
});
});
}
+(NSString *)validateString:(NSString *)string{
if (string == (id)[NSNull null] || string.length == 0 )
return #"";
return string;
}
+ (UIImage*)imageWithImage:(UIImage*)image
scaledToSize:(CGSize)newSize;
{
float proportion;
if (image.size.height > image.size.width) {
proportion = image.size.height / newSize.height;
} else {
proportion = image.size.width / newSize.width;
}
UIGraphicsBeginImageContext( newSize );
[image drawInRect:CGRectMake(newSize.width - (image.size.width/proportion),
newSize.height/2 - (image.size.height/proportion)/2,
image.size.width/proportion,
image.size.height/proportion)];
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
Using this code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *cellIdentifier = #"MyCell";
MyCell *cell = (MyCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
if ([MyHelper imageExistsForURL:photoURLString ]) {
UIImage *image = [UIImage imageWithContentsOfFile:[MyHelper pathForImage:photoURLString]];
eventImageView.image = [MyHelper imageWithImage:image scaledToSize:CGSizeMake(60, 60)];
} else {
[MyHelper setAsyncImage:eventImageView forDownloadImage:photoURLString ];
}
}
Since it is now clear that you are using oversized images, the solution is to figure out how big your images need to be in order to look good in your app.
There are several courses of action depending on how much you can change the server side portion of your system.
Use an image that is optimally sized for the highest-res case (3x) and let 2x and 1x devices do the scaling. This is again a bit wasteful.
Create some scheme whereby you will be able to get the right size image for your device type (perhaps by appending 2x, 3x etc.) to the image file name. Arguably the best choice.
Do the resizing on the client side. This can be somewhat CPU intensive and is probably the worst approach in my opinion because you will be doing a lot of work unnecessarily. However, if you can't change how your server works, then this is your only option.
Another problem with your code is that you are doing the resizing on the main/UI thread, which is blocking your UI, which is a no-no. Never perform long operations on the main thread.
You should be doing it on a background thread using dispatch_async or perhaps NSOperation and a sequential queue to reduce memory usage. Note that this can create new problems because you have to update your image view when the image is ready and consider things such as whether the cell is still visible or not. I came across a nice blog post on this a while back so I suggest searching the web.
However, if the images are really huge, then maybe you could consider setting up a proxy server and then getting resized images from there instead of the main server. Of course, you would have to consider intellectual property issues in this case.

defaultRepresentation fullScreenImage on ALAsset does not return full screen image

In my application I save images to an album as assets. I want also to retrieve them and display them in full screen. I use the following code :
ALAsset *lastPicture = [scrollArray objectAtIndex:iAsset];
ALAssetRepresentation *defaultRep = [lastPicture defaultRepresentation];
UIImage *image = [UIImage imageWithCGImage:[defaultRep fullScreenImage]
scale:[defaultRep scale] orientation:
(UIImageOrientation)[defaultRep orientation]];
The problem is that the image returned is nil. I have read at the ALAssetRepresentation reference that when the image does not fit it is returned nil.
I put this image to an UIImageView which has the size of the iPad screen. I was wondering if you could help me with this issue?
Thank you in advance.
I'm not a fan of fullScreenImage or fullResolutionImage. I found that when you do this on multiple assets in a queue, even if you release the UIImage immediately, memory usage will increase dramatically while it shouldn't. Also when using fullScreenImage or fullResolutionImage, the UIImage returned is still compressed, meaning that it will be decompressed before being drawn for the first time, thus on the main thread which will block your UI.
I prefer to use this method.
-(UIImage *)fullSizeImageForAssetRepresentation:(ALAssetRepresentation *)assetRepresentation
{
UIImage *result = nil;
NSData *data = nil;
uint8_t *buffer = (uint8_t *)malloc(sizeof(uint8_t)*[assetRepresentation size]);
if (buffer != NULL) {
NSError *error = nil;
NSUInteger bytesRead = [assetRepresentation getBytes:buffer fromOffset:0 length:[assetRepresentation size] error:&error];
data = [NSData dataWithBytes:buffer length:bytesRead];
free(buffer);
}
if ([data length])
{
CGImageSourceRef sourceRef = CGImageSourceCreateWithData((__bridge CFDataRef)data, nil);
NSMutableDictionary *options = [NSMutableDictionary dictionary];
[options setObject:(id)kCFBooleanTrue forKey:(id)kCGImageSourceShouldAllowFloat];
[options setObject:(id)kCFBooleanTrue forKey:(id)kCGImageSourceCreateThumbnailFromImageAlways];
[options setObject:(id)[NSNumber numberWithFloat:640.0f] forKey:(id)kCGImageSourceThumbnailMaxPixelSize];
//[options setObject:(id)kCFBooleanTrue forKey:(id)kCGImageSourceCreateThumbnailWithTransform];
CGImageRef imageRef = CGImageSourceCreateThumbnailAtIndex(sourceRef, 0, (__bridge CFDictionaryRef)options);
if (imageRef) {
result = [UIImage imageWithCGImage:imageRef scale:[assetRepresentation scale] orientation:(UIImageOrientation)[assetRepresentation orientation]];
CGImageRelease(imageRef);
}
if (sourceRef)
CFRelease(sourceRef);
}
return result;
}
You can use it like this:
// Get the full image in a background thread
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
UIImage* image = [self fullSizeImageForAssetRepresentation:asset.defaultRepresentation];
dispatch_async(dispatch_get_main_queue(), ^{
// Do something with the UIImage
});
});

Array with UIImageView elements - setImageWithURL

I have such code
arrayWithImages = [[NSMutableArray alloc] init];
NSEnumerator *enumForNames = [arrayWithNames objectEnumerator];
NSEnumerator *enumForURLs = [arrayWithURLs objectEnumerator];
id objName, objURL;
while(objName = [enumForNames nextObject]) {
objURL = [enumForURLs nextObject];
UIImageView *anImage = nil;
[anImage setImageWithURL:[NSURL URLWithString:objURL]];
(...)
[arrayWithImages addObject:anImage];
}
And each time I got SIGABRT in line with "[arrayWithImages addObject:anImage];"
What's here wrong?
I don’t see an setImageWithURL method on UIImageView. Where is this from?
Is there any output from the SIGABRT crash?
Try this code:
// Download the image
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:objURL]];
// Make an UIImage out of it
UIImage *anImage = [UIImage imageWithData:imageData];
// Create an image view with the image
UIImageView *imageView = [[UIImageView alloc] initWithImage:anImage];
// Check to make sure it exists
if (imageView != nil) {
// Add it to your array
[arrayWithImages addObject:imageView];
else {
NSLog(#"Image view is nil");
}
Note that you should download the images asynchronously to avoid hanging the loop. This blog post discussing asynchronous image downloading.
Also if you know that [enumForURLs nextObject]; will return an NSString (or even better, NSURL) you should assign objURL to type NSString (or NSURL).

Get camera roll images and their EXIF data?

I figured out how to get all the user's images in the camera roll using the AssestsLibrary:
- (void)updateLastPhotoThumbnail {
ALAssetsLibrary *assetsLibrary = [[ALAssetsLibrary alloc] init];
[assetsLibrary enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
NSInteger numberOfAssets = [group numberOfAssets];
if (numberOfAssets > 0) {
NSLog(#"numberOfPictures: %d",numberOfAssets);
//NSInteger lastIndex = numberOfAssets - 1;
int i = 0;
for (i = 0; i <= numberOfAssets-1; i++) {
[group enumerateAssetsAtIndexes:[NSIndexSet indexSetWithIndex:i] options:0 usingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {
UIImage *thumbnail = [UIImage imageWithCGImage:[result thumbnail]];
NSLog(#"theObject!!!! -- (%d) %#",i,thumbnail);
[cameraRollPictures addObject:thumbnail];
}];
}
}
} failureBlock:^(NSError *error) {
NSLog(#"error: %#", error);
}];
}
I successfully created the array of UIIMages, but how could I get EXIF data from the UIImages using this snippet I have?
PS: I've looked at this http://code.google.com/p/iphone-exif, but I cannot get it to build without errors.
You can't get metadata from UIImage objects.
But you can query an ALasset object for metadata:
NSDictionary *metadata = [[result defaultRepresentation] metadata];
Chefs,
Hendrik

ios assets not work on ios5

here is my code:
[library enumerateGroupsWithTypes:ALAssetsGroupAlbum
usingBlock:assetGroupEnumerator
failureBlock: ^(NSError *error) {
NSLog(#"Failure");
}];
my previous code is this:
assets = [[NSMutableArray alloc] init];
void (^assetEnumerator)( ALAsset *, NSUInteger, BOOL *) = ^(ALAsset *result, NSUInteger index, BOOL *stop) {
if(result != NULL) {
NSLog(#"See Asset: %#", result);
[assets addObject:result];
}
};
void (^assetGroupEnumerator)( ALAssetsGroup *, BOOL *) = ^(ALAssetsGroup *group, BOOL *stop) {
if(group != nil) {
[group enumerateAssetsUsingBlock:assetEnumerator];
}
[self.tableView reloadData];
};
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
and with my phone (ios5) doesn't work, can't go to the assetGroupEnumerator...
also in the simulator, the same thing....
any ideas?
thanks in advance
Are you releasing your ALAssetsLibrary before you are done displaying the assets? You need to retain the library throughout the lifetime of the groups and assets you are using.