Memory leak attaching multiple photos in MFMailComposerViewController - iphone

I am trying to attach multiple photos in a MailComposerViewController and I am using ALAssetPickerViewController to pick multiple photos. I have an NSMutableArray, which contains reference of selected assets. I am implementing a for loop which is enumerating over array of selected assets to get NSData of UIImage and UIImage is initialized with CGImageRef.
Code is as written below:
#autoreleasepool
{
NSString *emailTitle = #"Test";
NSString *messageBody = #"IOS programming is so fun!";
NSArray *toRecipents = [NSArray arrayWithObjects:#"abc#gmail.com", nil];
MFMailComposeViewController *tempmcvc = nil;
tempmcvc = [[MFMailComposeViewController alloc] init];
tempmcvc.mailComposeDelegate = self;
[tempmcvc setSubject:emailTitle];
[tempmcvc setMessageBody:messageBody isHTML:YES];
[tempmcvc setToRecipients:toRecipents];
tempmcvc.modalPresentationStyle=UIModalPresentationFullScreen;
tempmcvc.navigationBar.barStyle = UIBarStyleBlackOpaque;
for (AlAsset *assets in SelectedAssetsarray)
{
#autoreleasepool
{
UIImage *attachImagTemp = nil;
NSData *myData = nil;
CGImageRef iref = [assets.defaultRepresentation fullScreenImage];
NSString *nameOfImgTemp;
attachImagTemp = [UIImage imageWithCGImage:iref];
nameOfImgTemp = assets2.defaultRepresentation.filename;
myData = UIImageJPEGRepresentation (attachImagTemp, 1.0);
[tempmcvc addAttachmentData:myData mimeType:#"image/jpeg" fileName:nameOfImgTemp];
myData = nil;
attachImagTemp = nil;
iref = nil;
nameOfImgTemp = nil;
ALAsset *_temp = assets2;
_temp = nil;
}
}
}
dispatch_async(dispatch_get_main_queue(), ^(void) {
[self presentModalViewController:tempmcvc animated:YES]
});
Each asset I am attaching hardly is of 2 MB, but memory is constantly decreasing I am unable to release the memory properly; some memory is leaking please help to find the leak.

Try releasing attachment image with following code CGImageRelease(attachImagTemp); to release cache memory instead setting it as nil.
Also release tempmcvc object as suggested by Avi if you have not used ARC.

Related

Deallocating images created with UIGraphicsBeginImageContext

I'm creating images array for UIImageView so I can use [_myImageView startAnimating]; method. I figuree out that if I cache(preload) it like this way the animation is fluent.
But in Instruments I see, that game is deallocated but there is still too much memory allocated - I think there are some fragments of this UIGraphicsBeginContext stuff.
Am I using it right, is there some other way how to dealloc it?
Thanks in advance!
- (NSMutableArray *)generateCachedImageArrayWithFilename:(NSString *)filename extension:(NSString *)extension andImageCount:(int)count
{
_imagesArray = [[NSMutableArray alloc] init];
_fileExtension = extension;
_animationImageName = filename;
_imageCount = count;
for (int i = 0; i < count; i++)
{
NSString *tempImageNames = [NSString stringWithFormat:#"%#%i", filename, i];
NSString *imagePath = [[NSBundle mainBundle] pathForResource:tempImageNames ofType:extension];
UIImage *frameImage = [self loadRetinaImageIfAvailable:imagePath];
UIGraphicsBeginImageContext(frameImage.size);
CGRect rect = CGRectMake(0, 0, frameImage.size.width, frameImage.size.height);
[frameImage drawInRect:rect];
UIImage *renderedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[_imagesArray addObject:renderedImage];
if (_isDoublingFrames)
{
[_imagesArray addObject:renderedImage];
}
else if (_isTriplingFrames)
{
[_imagesArray addObject:renderedImage];
[_imagesArray addObject:renderedImage];
}
NSLog(#"filename = %#", filename);
}
return _imagesArray;
}
- (UIImage *)loadRetinaImageIfAvailable:(NSString *)path
{
NSString *retinaPath = [[path stringByDeletingLastPathComponent] stringByAppendingPathComponent:[NSString stringWithFormat:#"%##2x.%#", [[path lastPathComponent] stringByDeletingPathExtension], [path pathExtension]]];
if ([UIScreen mainScreen].scale == 2.0 && [[NSFileManager defaultManager] fileExistsAtPath:retinaPath] == YES)
return [[UIImage alloc] initWithCGImage:[[UIImage imageWithData:[NSData dataWithContentsOfFile:retinaPath]] CGImage] scale:2.0 orientation:UIImageOrientationUp];
else
return [UIImage imageWithContentsOfFile:path];
}
-(void) dealloc
{
[_imagesArray removeAllObjects];
_imagesArray = nil;
}
I had memory issues similar you wrote and what I would try out in your code
1) use #autoreleasepool
to wrap either the method or the content of the loop in #autoreleasepool { } to create a local pool which will be released after method is done. In your situation it seems to me wrapping the loop is better:
for (int i = 0; i < count; i++)
{
#autoreleasepool {
// the loop code
...
}}
2) kill [UIImage imageNamed]
Using imageNamed will prepare a cache (depending on the size of image) which will never be flushed out of the memory unless you reach a memory warning. Even if you set myImage.image = nil, the image is still in memory. XCode's Intruments tool Allocations view will show it nicely. What you can do is initialize UIImage like this, and iOS won't cache the image:
image = [UIImage imageWithContentsOfFile:fullpath];
You might use imageNamed in your code under loadRetinaImageIfAvailable.
Keep both two approach and let's check this out again!

UIImageView and UIScrollView load lot of pictures

In my app, I use UIImageView and UIScrollView to show a lot of images (every time there are about 20 images and every image is about 600px*500px and size is about 600kb).
I use this code to accomplish this function:
// Here is pictures Data;
self.klpArry = self.pictureData;
CGSize size = self.klpScrollView1.frame.size;
for (int i=0; i < [klpArr count]; i++) {
UIImageView *iv = [[UIImageView alloc] initWithFrame:CGRectMake((size.width * i)+300, 20, 546, 546)];
NSString *filePath = [[NSBundle mainBundle] pathForResource:[klpArr objectAtIndex:i] ofType:#"jpg"];
UIImage *imageData = [[UIImage alloc]initWithData:[NSData dataWithContentsOfFile:filePath]];
[iv setImage:imageData];
iv.backgroundColor = [UIColor grayColor];
[self.klpScrollView1 addSubview:iv];
imageData = nil;
iv = nil;
iv.image = nil;
filePath = nil;
[imageData release];
[filePath release];
[iv release];
}
// show the picture in scrollview;
[self.klpScrollView1 setContentSize:CGSizeMake(size.width * numImage, size.height)];
self.klpScrollView1.pagingEnabled = YES;
self.klpScrollView1.showsHorizontalScrollIndicator = NO;
self.klpScrollView1.backgroundColor = [UIColor grayColor];
But every time I initialize this function, the memory will increase about 5MB. Actually I release UIImageView, UIimage and UIScrollView (vi.image=nil, [vi release]) but it doesn't work, the allocated memory is not getting released. BTW, I used my friend's code first vi.image = nil then vi = nil; but the pictures are not getting displayed on scrollview.
The main problem, as I see it, is that you're setting your local variables to nil and then you proceed to try to use those local variables in methods like release and the like, but because they've been set to nil, those methods now do nothing, and the objects with the +1 retainCount (or now +2 because you've added them to your view) are never released.
Thus, I'd suggest the following:
//Here is pictures Data;
self.klpArry = self.pictureData;
CGSize size = self.klpScrollView1.frame.size;
for (int i=0; i < [klpArr count]; i++) {
UIImageView *iv = [[UIImageView alloc] initWithFrame:CGRectMake((size.width * i)+300, 20, 546, 546)];
NSString *filePath = [[NSBundle mainBundle] pathForResource:[klpArr objectAtIndex:i] ofType:#"jpg"];
UIImage *imageData = [[UIImage alloc]initWithData:[NSData dataWithContentsOfFile:filePath]];
[iv setImage:imageData];
iv.backgroundColor = [UIColor grayColor];
[self.klpScrollView1 addSubview:iv];
// Don't set these to nil, or else subsequent release statements do nothing!
// These statements are actually not necessary because they refer to local
// variables so you don't need to worry about dangling pointers. Make sure
// you're not confusing the purpose of setting a pointer to nil in ARC to
// what you're doing in your non-ARC code.
//
// imageData = nil;
// iv = nil;
// You don't want to set this to nil because if iv is not nil, your image
// will be removed from your imageview. So, not only is this not needed,
// but it's undesired.
//
// iv.image = nil;
// This statement is not necessary for the same reason you don't do it
// to imageData or iv, either. This is probably even worse, though, because
// filePath is not a variable that you initialized via alloc. You should
// only be releasing things you created with alloc (or new, copy, mutableCopy,
// for which you issued a retain statement).
//
// filePath = nil;
[imageData release];
// filePath is a +0 retainCount already, so don't release. You only release
// those items for which you increased retainCount (e.g. via new, copy,
// mutableCopy, or alloc or anything you manually retained).
//
// [filePath release];
[iv release];
}
// show the picture in scrollview;
[self.klpScrollView1 setContentSize:CGSizeMake(size.width * numImage, size.height)];
self.klpScrollView1.pagingEnabled = YES;
self.klpScrollView1.showsHorizontalScrollIndicator = NO;
self.klpScrollView1.backgroundColor = [UIColor grayColor];
Thus, your code would be simplified (and corrected) to probably just be:
//Here is pictures Data;
self.klpArry = self.pictureData;
CGSize size = self.klpScrollView1.frame.size;
for (int i=0; i < [klpArr count]; i++) {
UIImageView *iv = [[UIImageView alloc] initWithFrame:CGRectMake((size.width * i)+300, 20, 546, 546)];
NSString *filePath = [[NSBundle mainBundle] pathForResource:[klpArr objectAtIndex:i] ofType:#"jpg"];
UIImage *imageData = [[UIImage alloc] initWithData:[NSData dataWithContentsOfFile:filePath]];
[iv setImage:imageData];
iv.backgroundColor = [UIColor grayColor];
[self.klpScrollView1 addSubview:iv];
[imageData release];
[iv release];
}
// show the picture in scrollview;
[self.klpScrollView1 setContentSize:CGSizeMake(size.width * numImage, size.height)];
self.klpScrollView1.pagingEnabled = YES;
self.klpScrollView1.showsHorizontalScrollIndicator = NO;
self.klpScrollView1.backgroundColor = [UIColor grayColor];
Or you could further simplify your code through the use of autorelease:
//Here is pictures Data;
self.klpArry = self.pictureData;
CGSize size = self.klpScrollView1.frame.size;
for (int i=0; i < [klpArr count]; i++) {
UIImageView *iv = [[[UIImageView alloc] initWithFrame:CGRectMake((size.width * i)+300, 20, 546, 546)] autorelease];
NSString *filePath = [[NSBundle mainBundle] pathForResource:[klpArr objectAtIndex:i] ofType:#"jpg"];
UIImage *imageData = [[[UIImage alloc] initWithData:[NSData dataWithContentsOfFile:filePath]] autorelease];
[iv setImage:imageData];
iv.backgroundColor = [UIColor grayColor];
[self.klpScrollView1 addSubview:iv];
}
// show the picture in scrollview;
[self.klpScrollView1 setContentSize:CGSizeMake(size.width * numImage, size.height)];
self.klpScrollView1.pagingEnabled = YES;
self.klpScrollView1.showsHorizontalScrollIndicator = NO;
self.klpScrollView1.backgroundColor = [UIColor grayColor];
By the way, the statement (with autorelease):
UIImage *imageData = [[[UIImage alloc] initWithData:[NSData dataWithContentsOfFile:filePath]] autorelease];
could probably be simplified to:
UIImage *imageData = [UIImage imageWithContentsOfFile:filePath];
This gives you a UIImage, with a +0 retainCount (i.e. you don't have to release it) from your file.
So, a few final observations:
You really should probably review and study the Advanced Memory Management Programming Guide. It's dense reading if you're new to memory management, but mastery of these concepts (especially in non-ARC code) is critical.
If you haven't, I'd encourage you to run your code through the static analyzer ("Product" - "Analyze" or shift+command+B). I'd be surprised if many (if not most) of these issues wouldn't have been identified for you by the analyzer. You should have zero warnings when you run your code through the analyzer.
If you want to take this to the next level, you might want to be far more conservative about your memory management. The governing principle would be a system design that only loads the images that are needed at the UI at any given time, which involves not only calvinBhai's excellent suggestion of lazy loading of images (i.e. don't load images into memory until your UI really needs them), but also a pro-active releasing images once they've scrolled off the screen. Maybe you don't need to worry about it in your app, because you're only dealing with 20 images at a time, but if any of your portfolios/galleries expanded to 100 or 1000 images, the idea of keeping all of those in memory at any given time is just a bad idea. This is a more advanced concept, so maybe you should focus on the basic memory management problems of your existing code first, but longer term, you might want to contemplate lazy loading and pro-active releasing of images.
If memory is your concern,
try lazy loading the images = Load the visible image, the next and previous image. You dont have to have all images added to your klpscrollview.
Once you figure out lazy loading the images onto your scrollview, then you can think of fixing the images not showing on your scrollview.
Easier would be to search for "lazy load uiimage uiscrollview"

How to write exif metadata to an image (not the camera roll, just a UIImage or JPEG)

I am aware of how to save metadata using ALAssets. But, I want to save an image, or upload it somewhere, with exif intact. I have exif data as an NSDictionary. But how can I inject it properly into a UIImage (or probably an NSData JPEG representation)?
I am using UIImagePickerController to get the image from the camera and my flow is a bit different than the one described by Chiquis. Here it is:
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
UIImage *image = info[#"UIImagePickerControllerOriginalImage"];
NSString *fullPhotoFilename = ...; // generate the photo name and path here
NSData *photoData = [UIImage taggedImageData:image.jpegData metadata:info[#"UIImagePickerControllerMediaMetadata"] orientation:image.imageOrientation];
[photoData writeToFile:fullPhotoFilename atomically:YES];
}
And using a UIImage category to put combine the image data with its metadata:
#import <ImageIO/ImageIO.h>
#import "UIImage+Tagging.h"
#import "LocationHelper.h"
#implementation UIImage (Tagging)
+ (NSData *)writeMetadataIntoImageData:(NSData *)imageData metadata:(NSMutableDictionary *)metadata {
// create an imagesourceref
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef) imageData, NULL);
// this is the type of image (e.g., public.jpeg)
CFStringRef UTI = CGImageSourceGetType(source);
// create a new data object and write the new image into it
NSMutableData *dest_data = [NSMutableData data];
CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)dest_data, UTI, 1, NULL);
if (!destination) {
NSLog(#"Error: Could not create image destination");
}
// add the image contained in the image source to the destination, overidding the old metadata with our modified metadata
CGImageDestinationAddImageFromSource(destination, source, 0, (__bridge CFDictionaryRef) metadata);
BOOL success = NO;
success = CGImageDestinationFinalize(destination);
if (!success) {
NSLog(#"Error: Could not create data from image destination");
}
CFRelease(destination);
CFRelease(source);
return dest_data;
}
+ (NSData *)taggedImageData:(NSData *)imageData metadata:(NSDictionary *)metadata orientation:(UIImageOrientation)orientation {
CLLocationManager *locationManager = [CLLocationManager new];
CLLocation *location = [locationManager location];
NSMutableDictionary *newMetadata = [NSMutableDictionary dictionaryWithDictionary:metadata];
if (!newMetadata[(NSString *)kCGImagePropertyGPSDictionary] && location) {
newMetadata[(NSString *)kCGImagePropertyGPSDictionary] = [LocationHelper gpsDictionaryForLocation:location];
}
// Reference: http://sylvana.net/jpegcrop/exif_orientation.html
int newOrientation;
switch (orientation) {
case UIImageOrientationUp:
newOrientation = 1;
break;
case UIImageOrientationDown:
newOrientation = 3;
break;
case UIImageOrientationLeft:
newOrientation = 8;
break;
case UIImageOrientationRight:
newOrientation = 6;
break;
case UIImageOrientationUpMirrored:
newOrientation = 2;
break;
case UIImageOrientationDownMirrored:
newOrientation = 4;
break;
case UIImageOrientationLeftMirrored:
newOrientation = 5;
break;
case UIImageOrientationRightMirrored:
newOrientation = 7;
break;
default:
newOrientation = -1;
}
if (newOrientation != -1) {
newMetadata[(NSString *)kCGImagePropertyOrientation] = #(newOrientation);
}
NSData *newImageData = [self writeMetadataIntoImageData:imageData metadata:newMetadata];
return newImageData;
}
And finally, here is the method I am using to generate the needed GPS dictionary:
+ (NSDictionary *)gpsDictionaryForLocation:(CLLocation *)location {
NSTimeZone *timeZone = [NSTimeZone timeZoneWithName:#"UTC"];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setTimeZone:timeZone];
[formatter setDateFormat:#"HH:mm:ss.SS"];
NSDictionary *gpsDict = #{(NSString *)kCGImagePropertyGPSLatitude: #(fabs(location.coordinate.latitude)),
(NSString *)kCGImagePropertyGPSLatitudeRef: ((location.coordinate.latitude >= 0) ? #"N" : #"S"),
(NSString *)kCGImagePropertyGPSLongitude: #(fabs(location.coordinate.longitude)),
(NSString *)kCGImagePropertyGPSLongitudeRef: ((location.coordinate.longitude >= 0) ? #"E" : #"W"),
(NSString *)kCGImagePropertyGPSTimeStamp: [formatter stringFromDate:[location timestamp]],
(NSString *)kCGImagePropertyGPSAltitude: #(fabs(location.altitude)),
};
return gpsDict;
}
Hope it helps someone. Thanks to Gustavo Ambrozio, Chiquis and several others SO members I was able to piece it together and use it in my project.
UIImage does not contain metadata information (it is stripped). So if you want to save it without using the imagepicker method (not in camera roll):
Follow the answer here to write to a file with the metadata intact:
Problem setting exif data for an image
no idea why would this be downvoted but here is the method:
In this case im getting the image through AVFoundation and this is what goes in the
[[self stillImageOutput] captureStillImageAsynchronouslyFromConnection:videoConnection
completionHandler:^(CMSampleBufferRef imageSampleBuffer, NSError *error)
{
// code here
}
block code:
CFDictionaryRef metaDict = CMCopyDictionaryOfAttachments(NULL, imageSampleBuffer, kCMAttachmentMode_ShouldPropagate);
CFMutableDictionaryRef mutable = CFDictionaryCreateMutableCopy(NULL, 0, metaDict);
// Create formatted date
NSTimeZone *timeZone = [NSTimeZone timeZoneWithName:#"UTC"];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setTimeZone:timeZone];
[formatter setDateFormat:#"HH:mm:ss.SS"];
// Create GPS Dictionary
NSDictionary *gpsDict = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithFloat:fabs(loc.coordinate.latitude)], kCGImagePropertyGPSLatitude
, ((loc.coordinate.latitude >= 0) ? #"N" : #"S"), kCGImagePropertyGPSLatitudeRef
, [NSNumber numberWithFloat:fabs(loc.coordinate.longitude)], kCGImagePropertyGPSLongitude
, ((loc.coordinate.longitude >= 0) ? #"E" : #"W"), kCGImagePropertyGPSLongitudeRef
, [formatter stringFromDate:[loc timestamp]], kCGImagePropertyGPSTimeStamp
, [NSNumber numberWithFloat:fabs(loc.altitude)], kCGImagePropertyGPSAltitude
, nil];
// The gps info goes into the gps metadata part
CFDictionarySetValue(mutable, kCGImagePropertyGPSDictionary, (__bridge void *)gpsDict);
// Here just as an example im adding the attitude matrix in the exif comment metadata
CMRotationMatrix m = att.rotationMatrix;
GLKMatrix4 attMat = GLKMatrix4Make(m.m11, m.m12, m.m13, 0, m.m21, m.m22, m.m23, 0, m.m31, m.m32, m.m33, 0, 0, 0, 0, 1);
NSMutableDictionary *EXIFDictionary = (__bridge NSMutableDictionary*)CFDictionaryGetValue(mutable, kCGImagePropertyExifDictionary);
[EXIFDictionary setValue:NSStringFromGLKMatrix4(attMat) forKey:(NSString *)kCGImagePropertyExifUserComment];
CFDictionarySetValue(mutable, kCGImagePropertyExifDictionary, (__bridge void *)EXIFDictionary);
NSData *jpeg = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer] ;
After this code you will have your image in the jpeg nsdata and the correspoding dictionary for that image in the mutable cfdictionary.
All you have to do now is:
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)jpeg, NULL);
CFStringRef UTI = CGImageSourceGetType(source); //this is the type of image (e.g., public.jpeg)
NSMutableData *dest_data = [NSMutableData data];
CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)dest_data,UTI,1,NULL);
if(!destination) {
NSLog(#"***Could not create image destination ***");
}
//add the image contained in the image source to the destination, overidding the old metadata with our modified metadata
CGImageDestinationAddImageFromSource(destination,source,0, (CFDictionaryRef) mutable);
//tell the destination to write the image data and metadata into our data object.
//It will return false if something goes wrong
BOOL success = CGImageDestinationFinalize(destination);
if(!success) {
NSLog(#"***Could not create data from image destination ***");
}
//now we have the data ready to go, so do whatever you want with it
//here we just write it to disk at the same path we were passed
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0]; // Get documents folder
NSString *dataPath = [documentsDirectory stringByAppendingPathComponent:#"ImagesFolder"];
NSError *error;
if (![[NSFileManager defaultManager] fileExistsAtPath:dataPath])
[[NSFileManager defaultManager] createDirectoryAtPath:dataPath withIntermediateDirectories:NO attributes:nil error:&error]; //Create folder
// NSString *imageName = #"ImageName";
NSString *fullPath = [dataPath stringByAppendingPathComponent:[NSString stringWithFormat:#"%#.jpg", name]]; //add our image to the path
[dest_data writeToFile:fullPath atomically:YES];
//cleanup
CFRelease(destination);
CFRelease(source);
Note how I'm not saving using the ALAssets but directly into a folder of my choice.
Btw most of this code can be found in the link I posted at first.
There is easier way. If you need to save some exif, you can use SimpleExif pod
First create a ExifContainer:
ExifContainer *container = [[ExifContainer alloc] init];
and populate it with all requred data:
[container addUserComment:#"A long time ago, in a galaxy far, far away"];
[container addCreationDate:[NSDate dateWithTimeIntervalSinceNow:-10000000]];
[container addLocation:locations[0]];
Then you can add this data to image:
NSData *imageData = [[UIImage imageNamed:#"DemoImage"] addExif:container];
Then you just save this data as a JPEG
I faced the same problem, now I can upload files with EXIF data, also you can compress photo if need it, this solved the issue for me:
// Get your image.
UIImage *loImgPhoto = [self getImageFromAsset:loPHAsset];
// Get your metadata (includes the EXIF data).
CGImageSourceRef loImageOriginalSource = CGImageSourceCreateWithData(( CFDataRef) loDataFotoOriginal, NULL);
NSDictionary *loDicMetadata = (__bridge NSDictionary *) CGImageSourceCopyPropertiesAtIndex(loImageOriginalSource, 0, NULL);
// Set your compression quality (0.0 to 1.0).
NSMutableDictionary *loDicMutableMetadata = [loDicMetadata mutableCopy];
[loDicMutableMetadata setObject:#(lfCompressionQualityValue) forKey:(__bridge NSString *)kCGImageDestinationLossyCompressionQuality];
// Create an image destination.
NSMutableData *loNewImageDataWithExif = [NSMutableData data];
CGImageDestinationRef loImgDestination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)loNewImageDataWithExif, CGImageSourceGetType(loImageOriginalSource), 1, NULL);
// Add your image to the destination.
CGImageDestinationAddImage(loImgDestination, loImgPhoto.CGImage, (__bridge CFDictionaryRef) loDicMutableMetadata);
// Finalize the destination.
if (CGImageDestinationFinalize(loImgDestination))
{
NSLog(#"Successful image creation.");
// process the image rendering, adjustment data creation and finalize the asset edit.
//Upload photo with EXIF metadata
[self myUploadMethod:loNewImageDataWithExif];
}
else
{
NSLog(#"Error -> failed to finalize the image.");
}
CFRelease(loImageOriginalSource);
CFRelease(loImgDestination);
getImageFromAsset method:
-(UIImage *)getImageFromAsset:(PHAsset *)aPHAsset
{
__block UIImage *limgImageResult;
PHImageRequestOptions *lPHImageRequestOptions = [PHImageRequestOptions new];
lPHImageRequestOptions.synchronous = YES;
[self.imageManager requestImageForAsset:aPHAsset
targetSize:PHImageManagerMaximumSize
contentMode:PHImageContentModeDefault//PHImageContentModeAspectFit
options:lPHImageRequestOptions
resultHandler:^(UIImage *limgImage, NSDictionary *info) {
limgImageResult = limgImage;
}];
return limgImageResult;
}
Here's the basics of setting Make and Model metadata on a .jpg file in Swift 3 https://gist.github.com/lacyrhoades/09d8a367125b6225df5038aec68ed9e7 The higher level versions, like using ExifContainer pod, did not work for me.

issue with AlAsset library

I'm facing an issue with the ALAsset library: I have an UIView with 100 image views. When the view is loading, i'm calling a function for generating the images from the file name.
This is my class:
#interface myClass
{
NSString *fileName;
int pathId;
}
viewDidLoad
-(void)viewDidLoad
{
NSMutableArray *imageCollectionArray = [self createImage:arrayOfmyClassObject];
//Here I'm binding the 100 images in UIView using the images in imageCollectionArray
}
This is my method in which I found the issue:
- (NSMutableArray *)createImage:(NSMutableArray *)imageFileNamesArray
{
imageArray = [[NSMutableArray alloc] init];
for (int imageNameKey = 0; imageNameKey<100; imageNameKey++)
{
myClass *obj= [imageFileNamesArray objectAtIndex:imageNameKey];
if(obj.pathId == 0)
{
//Here adding the bundle image into the imageArray
[imageArray addObject:[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:obj.fileName ofType:#"png" inDirectory:#"Images"]]];
}
else
{
typedef void (^ALAssetsLibraryAssetForURLResultBlock)(ALAsset *asset); typedef void (^ALAssetsLibraryAccessFailureBlock)(NSError *error);
ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *myasset) {
};
ALAssetRepresentation *rep = [myasset defaultRepresentation];
CGImageRef iref = [rep fullResolutionImage];
UIImage *images;
if (iref)
{
//Here adding the photo library image into the imageArray
images = [UIImage imageWithCGImage:iref];
[imageArray addObject:images];
}
else
{
//Here adding the Nofile.png image into the imageArray if didn't find a photo library image
images = [UIImage imageNamed:#"Nofile.png"];
[imageArray addObject:images];
}
ALAssetsLibraryAccessFailureBlock failureblock = ^(NSError *myerror) {
//Here adding the Nofile.png image into the imageArray if any failure occurs
[imageArray addObject:[UIImage imageNamed:#"Nofile.png"]];
NSLog(#"booya, cant get image - %#",[myerror localizedDescription]);
};
NSURL *asseturl = [NSURL URLWithString:obj.fileName];
ALAssetsLibrary* assetslibrary = [[[ALAssetsLibrary alloc] init] autorelease];
[assetslibrary assetForURL:asseturl
resultBlock:resultblock failureBlock:failureblock];
}
}
return imageArray;
}
The problem was when I loads the view at first time the asset library images are not generating, only bundle images were displayed, if i go to any of the another view and return back to 100 image view then the asset images are generated.And works fine. The problem is the same function is not generating asset images at the first load. How can i fix this? Thanks in advance.
All methods related to ALAssetLibrary are asynchronous, so your view may complete its loading life cycle before the desired data is returned. You have to take this into account and redraw your view (or a portion of it) as needed.

iPad AQGridView not showing my UIImages not in bundle

I am using the ImageDemo template and it works great if I code in an image from the bundle but when I try and load them from directories I have created in my app I have no luck.
Code:
- (AQGridViewCell *) gridView: (AQGridView *) aGridView cellForItemAtIndex: (NSUInteger) index
{
static NSString * PlainCellIdentifier = #"PlainCellIdentifier";
AQGridViewCell * cell = nil;
ImageDemoGridViewCell * plainCell = (ImageDemoGridViewCell *)[aGridView dequeueReusableCellWithIdentifier: PlainCellIdentifier];
if ( plainCell == nil )
{
plainCell = [[[ImageDemoGridViewCell alloc] initWithFrame: CGRectMake(0.0, 0.0, 200.0, 150.0)
reuseIdentifier: PlainCellIdentifier] autorelease];
plainCell.selectionGlowColor = [UIColor lightGrayColor];
}
NSString *fileName = [NSString stringWithFormat:#"%#/%#_tn.jpg",[manufacturerID stringValue], [[[self.collectionItems objectAtIndex:index] valueForKey:#"PhotoName"] stringByReplacingOccurrencesOfString:#" " withString:#"%20"]];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath = [paths objectAtIndex:0];
NSString *savePath =[documentsPath stringByAppendingPathComponent:fileName] ;
NSData *imageData = nil;
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:savePath];
if (fileExists) {
imageData = [NSData dataWithContentsOfFile:savePath];
} else {
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"NoImageAvailableThumbNail" ofType:#"png"];
imageData = [NSData dataWithContentsOfFile:filePath];
}
UIImage *myImage = [UIImage imageWithData:imageData];
NSLog(#"%#", myImage);
if (myImage==nil) {
NSLog(#"NO IMAGE");
}
plainCell.image = myImage;
cell = plainCell;
return ( cell );
}
I code I am using to pull the image data I know works because I use it all over in other places in my app and I am checking for nil when I set the property on the AQGridViewCell:
- (void) setImage: (UIImage *) anImage
{
_imageView.image = anImage;
[self setNeedsLayout];
}
I don't see the check for a nil UIImage in your example there. Are you certain it's being loaded correctly? Try creating the image on one line and setting it on another so you can see it being created in the debugger. For reference, I do something similar with cached book cover images in the Kobo app, and that works. One difference though is that I use the C methods to load a UIImage from a PNG file. Something like UIImageFromPNGData(), off the top of my head (sorry replying by iPhone right now).
The only other thing I can think to check is whether the image view is hidden for some reason. Printing it in the debugger should tell you that, along with it's size, layout, etc.