I am opening the camera with UIImagePickerControllerSourceTypeCamera and a custom cameraOverlayView so I can take multiple photos without the "Use Photo" step.
This is working great, but there is a memory leak in the save photo function. Through a lot of debugging and research I have narrowed it down to the UIGraphicsGetImageFromCurrentImageContext function.
Here is a snippet of code where it happens:
UIGraphicsBeginImageContextWithOptions(timestampedImage.frame.size, timestampedImage.opaque, [[UIScreen mainScreen] scale]);
[[timestampedImage layer] renderInContext:UIGraphicsGetCurrentContext()];
UIImage *finalTimestampImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
I have scoured the internet and it seems that the UIGraphicsGetImageFromCurrentImageContext() function (quoted from this SO question) "returns a new autoreleased UIImage and points the finalTimestampImage ivar to it. The previously allocated UIImage is never actually released, the variable to it is just repointed to somewhere else."
I've tried so many solutions that have apparently worked for others:
Adding timestampedImage.layer.contents = nil; after UIGraphicsEndImageContext
Adding CGContextRef context = UIGraphicsGetCurrentContext(); and CGContextRelease(context); after UIGraphicsEndImageContext
Wrapping the above snippet in an NSAutoreleasePool
Wrapping the entire saveThisPhoto function in an NSAutoreleasePool
Creating an NSAutoreleasePool when the camera pops up and calling the [pool release] when didReceiveMemoryWarning is called
Closing the camera popup when didReceiveMemoryWarning is called, hoping it will clear the pool
Every possibly combination of the above
Yet everything I try, when I take photos I can see the Memory Utilized rising and not falling when I'm repeatedly taking photos on the device.
Does anyone know how I can release the autorelease object created by UIGraphicsGetImageFromCurrentImageContext?
Alternatively, is there an alternative way to make a UIImage out of an UIImageView?
Edit:
Here are the full functions as requested. There's a lot of extra releasing added in there just to try make sure everything is cleaned up. I have gone through and tested for the memory leak with each code block in saveThisPhoto systematically, and it only occurs when the UIGraphicsGetImageFromCurrentImageContext block (snippet above) is run.
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
NSLog(#"SAVING PHOTO");
[self saveThisPhoto:info];
picker = nil;
[picker release];
info = nil;
[info release];
}
- (void)saveThisPhoto:(NSDictionary *)info {
// Get photo count for filename so we're not overriding photos
int photoCount = 0;
if ([[NSUserDefaults standardUserDefaults] objectForKey:#"photocount"]) {
photoCount= [[[NSUserDefaults standardUserDefaults] objectForKey:#"photocount"] intValue];
photoCount++;
}
[[NSUserDefaults standardUserDefaults] setObject:[NSString stringWithFormat:#"%d", photoCount] forKey:#"photocount"];
[[NSUserDefaults standardUserDefaults] synchronize];
// Obtaining saving path
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *fileName = [NSString stringWithFormat:#"ri_%d.jpg", photoCount];
NSString *fileNameThumb = [NSString stringWithFormat:#"ri_%d_thumb.jpg", photoCount];
NSString *imagePath = [documentsDirectory stringByAppendingPathComponent:fileName];
NSString *imagePathThumb = [documentsDirectory stringByAppendingPathComponent:fileNameThumb];
// Extracting image from the picker and saving it
NSString *mediaType = [info objectForKey:UIImagePickerControllerMediaType];
// SAVE TO IPAD AND DB
if ([mediaType isEqualToString:#"public.image"]){
// Get Image
UIImage *editedImage = [info objectForKey:UIImagePickerControllerOriginalImage];
// Figure out image orientation
CGSize resizedSize;
CGSize thumbSize;
if (editedImage.size.height > editedImage.size.width) {
resizedSize = CGSizeMake(480, 640);
thumbSize = CGSizeMake(150, 200);
} else {
resizedSize = CGSizeMake(640, 480);
thumbSize = CGSizeMake(150, 113);
}
// MAKE NORMAL SIZE IMAGE
UIImage *editedImageResized = [editedImage resizedImage:resizedSize interpolationQuality:0.8];
// clean up the one we won't use any more
editedImage = nil;
[editedImage release];
// ADD TIMESTAMP TO IMAGE
// make the view
UIImageView *timestampedImage = [[UIImageView alloc] initWithImage:editedImageResized];
CGRect thisRect = CGRectMake(editedImageResized.size.width - 510, editedImageResized.size.height - 30, 500, 20);
// clean up
editedImageResized = nil;
[editedImageResized release];
// make the label
UILabel *timeLabel = [[UILabel alloc] initWithFrame:thisRect];
timeLabel.textAlignment = UITextAlignmentRight;
timeLabel.textColor = [UIColor yellowColor];
timeLabel.backgroundColor = [UIColor clearColor];
timeLabel.font = [UIFont fontWithName:#"Arial Rounded MT Bold" size:(25.0)];
timeLabel.text = [self getTodaysDateDatabaseFormat];
[timestampedImage addSubview:timeLabel];
// clean up what we won't use any more
timeLabel = nil;
[timeLabel release];
// make UIIMage out of the imageview -- MEMORY LEAK LOOKS LIKE IT IS IN THIS BLOCK
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
UIGraphicsBeginImageContextWithOptions(timestampedImage.frame.size, timestampedImage.opaque, [[UIScreen mainScreen] scale]);
[[timestampedImage layer] renderInContext:UIGraphicsGetCurrentContext()];
UIImage *finalTimestampImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
timestampedImage.layer.contents = nil;
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextRelease(context);
// clean up the one we won't use any more
timestampedImage = nil;
[timestampedImage release];
// SAVE NORMAL SIZE IMAGE TO DOCUMENTS FOLDER
NSData *webDataResized = UIImageJPEGRepresentation(finalTimestampImage, 1.0); // JPG
[webDataResized writeToFile:imagePath atomically:YES];
// clean up the one we won't use any more
finalTimestampImage = nil;
[finalTimestampImage release];
[pool release]; // to get rid of the context image that is stored
// SAVE TO DATABASE
[sqlite executeNonQuery:#"INSERT INTO inspection_images (agentid,groupid,inspectionid,areaid,filename,filenamethumb,filepath,orderid,type) VALUES (?, ?, ?, ?, ?, ?, ?, ?,?) ",
[NSNumber numberWithInt:loggedIn],
[NSNumber numberWithInt:loggedInGroup],
myInspectionID,
[[tableData objectAtIndex:alertDoMe] objectForKey:#"areaid"],
fileName,
fileNameThumb,
documentsDirectory,
[NSNumber numberWithInt:photoCount],
[NSNumber numberWithInt:isPCR]
];
// Clean up
webDataResized = nil;
[webDataResized release];
} else {
NSLog(#">>> IMAGE ***NOT*** SAVED");
}
NSLog(#"IMAGE SAVED - COMPLETE");
info = nil;
[info release];
}
You're setting your variables to nil before releasing them, and some are already auto released.
Normally when using release you should release and them set to nil.
[var release]
var = nil;
But in some of these you should not be calling release.
The following one is your main culprit.
// clean up the one we won't use any more
timestampedImage = nil;
[timestampedImage release];
// SAVE NORMAL SIZE IMAGE TO DOCUMENTS FOLDER
Related
Is there a way to get the filesize in KB from a UIImage, without getting that image from didFinishPickingMediaWithInfo? The images that are presented are coming from the photo album.
I tried the following code, but this gives the following result: size of image in KB: 0.000000
- (void)setImage:(UIImage *)image
{
_image = image;
self.imageView.image = image;
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self setupView];
self.backgroundColor = [UIColor whiteColor];
panGestureRecognizer = [[UIPanGestureRecognizer alloc]initWithTarget:self action:#selector(beingDragged:)];
[self addGestureRecognizer:panGestureRecognizer];
// prepare image view
self.imageView = [[UIImageView alloc]initWithFrame:self.bounds];
self.imageView.clipsToBounds = YES;
self.imageView.contentMode = UIViewContentModeScaleAspectFill;
[self addSubview:self.imageView];
NSData *imgData = [[NSData alloc] initWithData:UIImageJPEGRepresentation((_image), 0.5)];
int imageSize = imgData.length;
NSLog(#"size of image in KB: %f ", imageSize/1024.0);
overlayView = [[OverlayView alloc]initWithFrame:CGRectMake(self.frame.size.width/2-100, 0, 100, 100)];
overlayView.alpha = 0;
[self addSubview:overlayView];
}
return self;
}
Here is an example of calculating file sizes of files in your HomeDirectory or Documents:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:#"yourimagename.png"]
File sie is calculated:
filesize = [[[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil] fileSize];
NSLog(#"%lld",filesize);
Before you do that add filesize, you can add it in the .m file
#interface ViewController () {
long long filesize;
}
this will result in bytes, if you are trying to convert those bytes into kb you can use the NSByteCountFormatter and it will take care of all the math for you:
NSByteCountFormatter *sizeFormatter = [[NSByteCountFormatter alloc] init];
sizeFormatter.countStyle = NSByteCountFormatterCountStyleFile;
and then call it like so :
[sizeFormatter stringFromByteCount:filesize]
If the image is not saved on the disk you can calculate the size this way:
NSData *imgData = UIImageJPEGRepresentation(_image, 1);
filesize = [imgData length]; //filesize in this case will be an int not long long so use %d to NSLog it
initWithFrame: runs before setImage: is called, so _image is nil at the time you are doing your calculations. You could move them into the setImage: function...
However, this is a weird way of measuring the size of the image. A JPEG file and what ends up in graphics memory are widely different, so if you are doing it for profiling reasons, this is not going to give you any accurate measurements. If you just want the size on disk, you can simply check that through NSFileManager.
NSInteger imageDataSize = CGImageGetHeight(image.CGImage) * CGImageGetBytesPerRow(image.CGImage);
Before iOS 8, I didn't have problems with this & now, yes.
LOG:
Assertion failed: (CGFloatIsValid(x) && CGFloatIsValid(y)), function void CGPathMoveToPoint(CGMutablePathRef, const CGAffineTransform *, CGFloat, CGFloat), file Paths/CGPath.cc, line 254.
This is my code:
UIImage* image = nil;
CGSize imageSize = CGSizeMake(self.view.bounds.size.width, self.view.bounds.size.height);
UIGraphicsBeginImageContextWithOptions(imageSize, NO , 0.0f);
[self.view.layer renderInContext:UIGraphicsGetCurrentContext()]; // <- ERROR
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
My purpose is to convert the view to image.
Check for empty rectangles in what you're drawing, whether the view's bounds or the layer's content rectangle. I have noticed that assertion failure on iOS 8 where, before, empty rectangles were silently ignored.
I've added a number of...
if (!CGRectIsEmpty(bounds)) {
}
...conditions in my drawing.
We ran into this problem as well and tracked it down to a view that had a cornerRadius set on the CALayer, but had a zero size. In our case, this was only occurring on a device - not on the simulator. If you see _renderBorderInContext and CA_CGContextAddRoundRect in your backtrace then you're probably seeing the same thing.
A zero size in either dimension (height/width) will cause this error to occur if a corner radius is set. Unfortunately since it's an assertion it's not possible to catch the error and recover, so we're exploring the option of traversing the hierarchy prior to snapshotting to detect the case and recover by setting the cornerRadius to 0 and back after the call to renderInContext.
Works in IOS 8
UIGraphicsBeginImageContext(self.view.bounds.size);
[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docDir = [paths objectAtIndex:0];
fileName = [docDir stringByAppendingPathComponent:[NSString stringWithFormat:#"reporte.png"]];
NSData *imageData = UIImageJPEGRepresentation(image, 1.0);
NSError *error = NULL;
BOOL written =[imageData writeToFile:fileName atomically:YES];
if (!written)
NSLog(#"write failed, error=%#", error);
else{
[self sendPorCorreo];
}
While... I will solve the export of other way.
Regards.
- (IBAction)ibaExportar:(id)sender {
NSString *mystr = #"";
NSString *csvstr;
csvstr = [NSString stringWithFormat:#",Cliente,Domicilio,DueƱo"];
mystr = [NSString stringWithFormat:#"%#,%#,%#\n",self.numCliente,self.iboDomicilio.text,self.iboDueno.text];
csvstr = [NSString stringWithFormat:#"%#\n%#",csvstr,mystr];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docDir = [paths objectAtIndex:0];
fileName = [docDir stringByAppendingPathComponent:[NSString stringWithFormat:#"Reporte.csv"]];
NSError *error = NULL;
BOOL written = [csvstr writeToFile:fileName atomically:YES encoding:NSUTF8StringEncoding error:&error];
if (!written)
NSLog(#"write failed, error=%#", error);
else{
[self sendEmail];
}
}
- (void) sendEmail {
NSString*subject;
subject= [#"Reporte Cliente " stringByAppendingString:#""];
MFMailComposeViewController *picker = [[MFMailComposeViewController alloc] init];
picker.mailComposeDelegate = self;
[picker setSubject:subject];
NSData *dataFile = [NSData dataWithContentsOfFile:fileName];
[picker addAttachmentData:dataFile mimeType:#"text/csv" fileName:#"Reporte.csv"];
NSString *emailBody =subject;
[picker setMessageBody:emailBody isHTML:NO];
[self presentViewController:picker animated:YES completion:nil];
}
-(void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error {
[self dismissViewControllerAnimated:YES completion:nil];
}
Check your float with isnan(x) before using with Core Graphics.
According to the answer from tyler, I fix the problem. You can just find out the problematic view in self.view. The ios 8 not allow the zero size view to set the cornerRadius. So you must have a zero size view and set the cornerRadius for it. You can run the following code to find it out and fix it.
- (void)findZeroSizeControlWithSuperView:(UIView *)superView {
[self isProblematicCrotrol:superView];
[superView.subviews enumerateObjectsUsingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
[self isProblematicCrotrol:obj];
}];
}
- (BOOL)isProblematicCrotrol:(UIView *)view {
if ((view.frame.size.width == 0 || view.frame.size.height == 0) && view.layer.cornerRadius != 0) {
NSLog(#"this is the problematic view:%#", view);
return YES;
} else {
return NO;
}
}
In my project is using maximum 60 images and One of my feature is needs to be an automatically crop all the 60 images in a given Ratio. I'm using the for loop for this implementation.
Inside the for loop contains crop and save the images. It was Implemented. But My app Meets crash in device because of Due to Memory pressure. Please Help Me
for (int ref=0; ref<[_selectedPhotosCollectionthumb count];ref++)
{
UIScrollView *scrollView=[[UIScrollView alloc] initWithFrame:CGRectMake(0,biManager.screenSize.height/2,biManager.screenSize.width,biManager.screenSize.height/2)];
[scrollView setDelegate:self];
[scrollView setBackgroundColor:[UIColor clearColor]];
[self addSubview:scrollView];
// scrollView.backgroundColor=[UIColor blueColor];
scrollView.userInteractionEnabled=YES;
scrollView.scrollEnabled=YES;
scrollView.tag=ref;
scrollView.hidden=YES;
[_scrollViews addObject:scrollView];
NSLog(#"%i",[_selectedPhotosCollection count]);
NSMutableArray *arrayCell=[_productCollectionsDict valueForKey:[_selectedPhotosCollection objectAtIndex:ref]];
int heightV=0;
for (int cellIndex=0;cellIndex<[arrayCell count];cellIndex++)
{
PrintCellView *cellObj=[arrayCell objectAtIndex:cellIndex];
if(cellObj.pCount>0)
{
PrintEditCellView *cell;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
{
cell=[[PrintEditCellView alloc] initWithFrame:CGRectMake(0,heightV*100,biManager.screenSize.width,100)];
scrollView.contentSize=CGSizeMake(0,heightV*100+100);
cell.delegate=self;
}
else
{
cell=[[PrintEditCellView alloc] initWithFrame:CGRectMake(0,heightV*50,biManager.screenSize.width,50)];
scrollView.contentSize=CGSizeMake(0,heightV*50+50);
cell.delegate=self;
}
NSDate *now = [NSDate date];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateFormat = #"hh:mm:ss";
[dateFormatter setTimeZone:[NSTimeZone systemTimeZone]];
NSLog(#"The Current Time is %#",[dateFormatter stringFromDate:now]);
// NSData *imageData=UIImageJPEGRepresentation(Thumbimage,1.0);
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString* path = [documentsDirectory stringByAppendingPathComponent:[_selectedPhotosCollection objectAtIndex:ref]];
NSData *data = [[NSMutableData alloc] initWithContentsOfFile:path];
UIImage *image1=[[UIImage alloc]initWithData:data];
cell.productName.text=cellObj.productName.text;
UIImage * image=[self imageByCropping:image1 CropRatio:cell.productName.text];
NSLog(#"CROPPPP");
NSData *imageData= [[NSData alloc] initWithData:UIImageJPEGRepresentation(image,1.0)];
//
NSString* path1 = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"Prydex%i%#.jpg",cellIndex,[dateFormatter stringFromDate:now]]];
NSLog(#"pthhh:%#",path1);
[imageData writeToFile:path1 atomically:YES];
cell.editedImageURL=path1;
NSLog(#"%#,%i",cellObj.productName.text,cellObj.pCount);
[scrollView addSubview:cell];
[cell release];
heightV=heightV+1;
[dateFormatter release];
[image1 release];
// [imageData release];
// [image release];
}
}
//NSLog(#"Scroll Count %i",[_scrollViews count]);
for (UIScrollView *scrollView in _scrollViews)
{
if (scrollView.tag==0)
{
scrollView.hidden=NO;
}
else
{
scrollView.hidden=YES;
}
}
[SVProgressHUD dismiss];
}
Cropping Code
- (UIImage *)imageByCropping:(UIImage *)image CropRatio:(NSString*)ratio
{
CGSize size;
NSArray *array=[ratio componentsSeparatedByString:#"*"];
NSString *productWidth=[array objectAtIndex:0];
NSString *productHeight=[array objectAtIndex:1];
NSLog(#"SIZE:%#,%#",productWidth,productHeight);
NSLog(#"SIZE:%f,%f",image.size.width,image.size.height);
if (image.size.width/[productWidth intValue]>=230)
{
if (image.size.height/[productHeight intValue]>=230) {
size=CGSizeMake([productWidth intValue]*230,[productHeight intValue]*230);
NSLog(#"SIZE Inner:%i,%i",[productWidth intValue],[productHeight intValue]);
}
else if(image.size.width/[productWidth intValue]>=100)
{
if (image.size.height/[productHeight intValue]>=100)
{
size=CGSizeMake([productWidth intValue]*100,[productHeight intValue]*100);
NSLog(#"SIZE outer:%i,%i",[productWidth intValue],[productHeight intValue] );
}
}
}
else if(image.size.width/[productWidth intValue]>=100)
{
if (image.size.height/[productHeight intValue]>=100)
{
size=CGSizeMake([productWidth intValue]*100,[productHeight intValue]*100);
NSLog(#"SIZE outer:%i,%i",[productWidth intValue],[productHeight intValue] );
}
}
NSLog(#"crop---->%#",NSStringFromCGSize(size));
double x = (image.size.width - size.width) / 2.0;
double y = (image.size.height - size.height) / 2.0;
CGRect cropRect = CGRectMake(x, y, size.width, size.height);
CGImageRef imageRef = CGImageCreateWithImageInRect([image CGImage], cropRect);
UIImage *cropped = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
return cropped;
}
The solution might be using recursion:
Create a method that takes an array of images you need to process. Inside the method check if array count is zero.
If it is empty you should return, possibly doing some callback to notify the application your image processing is done.
If the array is not empty, take the first image from the array, do all the processing, then remove the first object from the array and call the same method with the new array missing that element. The call should be kind of
[self performSelector:#selector(methodName:) withObject:imageArray];
All together should look something like this:
- (void)processImages:(NSArray *)images {
if(images.count < 1) {
[self performSelectorOnMainThread:#selector(imageProcessingDone) withObject:nil waitUntilDone:NO];
}
else {
UIImage *toProcess = images[0];
NSMutableArray *newArray = [images mutableCopy];
[newArray removeObjectAtIndex:0];
//do the processing
[self performSelector:#selector(processImages:) withObject:newArray];
}
}
I am developing one app in that getting images from array and display vertically in ScrollView.
when user double tapped on particular image i want that exact image store into plist according to tag value of that image, and retrieve that image later on when require.
i tried this one
// Store Data into plist.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask,
YES);
NSString *path = [NSString stringWithFormat:#"%#/myPlist.plist",
[paths objectAtIndex:0]];
// Place an image in a dictionary that will be stored as a plist
NSMutableDictionary * dictionary=[[NSMutableDictionary alloc]init];
[dictionary setObject:ImgView.tag forKey:#"image"];
NSLog(#"%#",dictionary);
// Write the dictionary to the filesystem as a plist
[NSKeyedArchiver archiveRootObject:dictionary toFile:path];
// For getting data from NSmutable array store it to the scrollview.
int m=0;
AppDelegate * delegate=(AppDelegate *)[[UIApplication sharedApplication]delegate];
delegate.front=TRUE;
delegate.back=FALSE;
UIScrollView *scrollView=[[UIScrollView alloc]initWithFrame:CGRectMake(0, 0, 320, 480)];
[scrollView setPagingEnabled:YES];
[scrollView setShowsHorizontalScrollIndicator:NO];
FrontsCards=[[NSMutableArray alloc]initWithObjects:#"cloub1.png",#"cloub2.png",#"cloub3.png",#"cloub4.png",#"cloub5.png",#"cloub6.png",#"cloub7.png",#"cloub8.png",#"cloub9.png",#"cloub10.png",#"cloub11.png",#"cloub12.png",#"diamond1.png",#"diamond2.png",#"diamond3.png",#"diamond4.png",#"diamond5.png", nil];
for(m=0; m<[FrontsCards count];m++)
{
ImgView.alpha=1;
ImgView.tag=m;
int randIdx=arc4random()%[FrontsCards count];
NSString *imageName=[FrontsCards objectAtIndex:randIdx];
NSString *fullImageName=[NSString stringWithFormat:#"%#",imageName];
int padding=0;
CGRect imageViewFrame=CGRectMake(scrollView.frame.size.width*m+padding, scrollView.frame.origin.y, scrollView.frame.size.width-2*padding, scrollView.frame.size.height);
ImgView=[[UIImageView alloc]initWithFrame:imageViewFrame];
[ImgView setImage:[UIImage imageNamed:fullImageName]];
NSLog(#"%d",m);
// Place an image in a dictionary that will be stored as a plist
//[dictionary setObject:image forKey:#"image"];
// Write the dictionary to the filesystem as a plist
//[NSKeyedArchiver archiveRootObject:dictionary toFile:path];
[scrollView addSubview:ImgView];
UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(doubleTapImgView:)];
doubleTap.numberOfTapsRequired = 2;
doubleTap.delegate = self;
[self.ImgView addGestureRecognizer:doubleTap];
self.ImgView.userInteractionEnabled=YES;
}
CGSize scrollViewSize=CGSizeMake(scrollView.frame.size.width*[FrontsCards count], scrollView.frame.size.height);
[scrollView setContentSize:scrollViewSize];
[self.view addSubview:scrollView];
help me out this thanks in advance.
Define this MACRO Definition at the top of your .m file
#define LIB_DIR_PATH NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES)[0]
Use this function to Save Image to Plist with Image and Name
- (void)saveImage:(UIImage *)image WithName:(NSString *)imageName
{
// If File Exist then read it otherwise creat new
NSMutableDictionary *imageInfoDict;
if([[NSFileManager defaultManager] fileExistsAtPath:[LIB_DIR_PATH stringByAppendingPathComponent:#"imageInfo.plist"]])
{
NSData *fileData = [NSData dataWithContentsOfFile:[LIB_DIR_PATH stringByAppendingPathComponent:#"imageInfo.plist"]];
imageInfoDict = [NSMutableDictionary dictionaryWithDictionary:[NSKeyedUnarchiver unarchiveObjectWithData:fileData]];
}
else
imageInfoDict = [NSMutableDictionary dictionaryWithCapacity:0];
// Add Single Image to Dictionary
[imageInfoDict setValue:image forKey:imageName];
// Convert Main info Dictionary to `NSData` to Save on Disc
[NSKeyedArchiver archiveRootObject:imageInfoDict toFile:[LIB_DIR_PATH stringByAppendingPathComponent:#"imageInfo.plist"]];
// To Read Stored Image Use Following Code
[self readImageFromPlistByKey:imageName];
}
This function returns image for respective name from Plist
-(UIImage *)readImageFromPlistByKey:(NSString *)keyName
{
// If File Exist then read it otherwise creat new
NSMutableDictionary *imageInfoDict;
if([[NSFileManager defaultManager] fileExistsAtPath:[LIB_DIR_PATH stringByAppendingPathComponent:#"imageInfo.plist"]])
{
NSData *fileData = [NSData dataWithContentsOfFile:[LIB_DIR_PATH stringByAppendingPathComponent:#"imageInfo.plist"]];
if([fileData length] > 0)
{
// Read Plist
imageInfoDict = [NSMutableDictionary dictionaryWithDictionary:[NSKeyedUnarchiver unarchiveObjectWithData:fileData]];
// Here is your Image
return imageInfoDict[keyName];
}
}
else
{
// Return Default Image if not Found
return [UIImage imageNamed:#"Default.png"];
}
}
If you are going to just store indexes, you need to have a master imageArray. I added insert/delete when user double taps the imageView twice.
- (void)doubleTapImgView:(UITapGestureRecognizer *)recognizer
{
UIImageView *imageView = (UIImageView *)recognizer.view;
[self insertorDeleteImageIndex:imageView.tag-1];
}
- (NSString *)plistFilePath{
NSString *documents = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
return [documents stringByAppendingPathComponent:#"ImageIndexes.plist"];
}
- (void)insertorDeleteImageIndex:(NSInteger)index{
NSString *filePath = [self plistFilePath];
NSMutableArray *savedIndexes = [NSMutableArray arrayWithContentsOfFile:filePath];
if (!savedIndexes) {
savedIndexes = [NSMutableArray array];
}
if (![savedIndexes containsObject:#(index)]) {
[savedIndexes addObject:#(index)];
}else{
[savedIndexes removeObject:#(index)];
}
[savedIndexes writeToFile:filePath atomically:YES];
}
- (NSArray *)savedImageIndexes{
NSString *filePath = [self plistFilePath];
return [NSArray arrayWithContentsOfFile:filePath];
}
Source code
The code you post above can't be the real code as it wouldn't compile. That said, it shows a few errors:
You can't put basic numbers (NSInteger) into a dictionary, it needs to be boxed in an NSNumber.
You're setting the tag of the image before you create the instance of the image view (so either it will do nothing or set the wrong tag).
For saving the image, if you do want to save the image instead of the tag, you need to save it as data. You can store an image inside a dictionary no problem, but if you then want to store your dictionary as a plist you need to convert the image to NSData. You can get the image data using:
UIImageJPEGRepresentation(imageToSave, 0.8)
Hi I have some code that mostly works. It basically animates, with a fade affect, number of images from an array.
Basically it all works well however I have a slight bug that when the view first loads the first image only in the array seems to rotate 90 degrees to the correct position and then fades to the same image but its annoying to see animate like this. All the fading from image to another is perfect just with the above mentioned bug. And as mentioned is just on first load.
Here is the code. images is an MutableArray and both topImageView and bottomImageView both are ivars and ** synthesized**. So is the images array, too.
int topIndex = 0, bottomIndex = 1;
-(void)imageFader{
self.imageViewBottom = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 320, 370)];
[self.view addSubview:imageViewBottom];
[imageViewBottom setAlpha:0.0];
[imageViewBottom release];
self.imageViewTop = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 320, 370)];
[self.view addSubview:imageViewTop];
[imageViewTop setAlpha:1.0];
[imageViewTop release];
[imageViewTop setImage:[images objectAtIndex:topIndex]];
timer = [NSTimer timerWithTimeInterval:1.0
target:self
selector:#selector(onTimer)
userInfo:nil
repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
[timer fire];
}
-(void)onTimer{
[UIView animateWithDuration:1 animations:^{
//set the image of the image that is not currently visible
if (imageViewTop.alpha == 0) {
[imageViewTop setImage:[images objectAtIndex:topIndex]];
}
if (imageViewBottom.alpha == 0) {
[imageViewBottom setImage:[images objectAtIndex:bottomIndex]];
}
//make sure the images rotate
[imageViewTop setAlpha:imageViewTop.alpha == 0 ? 1 : 0];
[imageViewBottom setAlpha:imageViewBottom.alpha == 0 ? 1 : 0];
//make sure the images play in a loop
topIndex = topIndex < [images count]-1 ? bottomIndex+1 : 0;
bottomIndex = bottomIndex < [images count]-1 ? bottomIndex+1 : 0;}];
}
And here is how the images are saved to the docs directory before being loaded in to the MutableArray for the animation.
-(void) imagePickerController:(UIImagePickerController *) picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
if (picker.sourceType == UIImagePickerControllerSourceTypeCamera) {
UIImage *image = [info objectForKey:#"UIImagePickerControllerOriginalImage"];
NSMutableDictionary *metadata = [[NSMutableDictionary alloc] init];
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
//Save to camera roll
[library writeImageToSavedPhotosAlbum:[image CGImage] orientation:(ALAssetOrientation) ALAssetOrientationRight completionBlock:^(NSURL *assetURL, NSError *error) { NSLog(#"completionBlock");
}];
int imageNumber = 0;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *pathToFile;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
do
{
// increment the image
imageNumber++;
// get the new path to the file
pathToFile = [documentsDirectory stringByAppendingPathComponent:
[NSString stringWithFormat:
#"standingImage%d.JPG", imageNumber]];
}
while([fileManager fileExistsAtPath:pathToFile]);
/* so, we loop for as long as we keep coming up with names that already exist */
UIImage *image2 = image; // imageView is my image from camera
//Use UIImageJPEGRepresentation to maitain image orientation!
NSData *imageData = UIImageJPEGRepresentation(image2, 0.9);
[imageData writeToFile:pathToFile atomically:NO];
[self dismissModalViewControllerAnimated:YES];
This unexpected orientation may be caused by the origin of the images - the UIImagePickerController. Try accessing the JPEG files you mentioned in your comment and check their orientation, then you'll at least narrow down the potential cause of your issue.