I currently am using the code I found on an online tutorial to have the iPhone user select an image from their camera roll. Setting the image to the UIImageView works fine, but then I try to call a function on the image (every time a slider is moved). It turns out that the operating system is not holding onto the image, so when I try to access it (or the caching variable I made), it doesn't work (because the address gets set to 0x0000000).
If UIImage conformed to NSCopying, this would be simple. But, it doesn't. So, how do I copy an image so the operating system doesn't delete it?
Possible Duplicates
How do I make an exact copy of a UIImage returned from a UIImagePickerController?
Making deep copy of UIImage
EDIT:
The slider, when changed, calls this function:
- (IBAction)samplingSliderChanged:(id)sender {
NSLog(#"%#", self.imageStay);
float sample = self.samplingSlider.value * 0.6 + 0.2;
self.samplingRateText.text = [NSString stringWithFormat:#"%.0f%%", sample*100];
//self.imageView.image = [self.brain sampleImage:self.imageStay atRate:sample];
self.imageView.image = [self.brain sampleImage:self.imageStay.copy atRate:sample];
NSLog(#"self.imageStay: %#, self.imageStay.copy: %#", self.imageStay, self.imageStay.copy);
}
The first time the slider is moved, my self.imageStay (the cache variable) has an address of 0x0000.
My show-the-camera-roll function is below:
-(void)imagePickerController:(UIImagePickerController *)picker
didFinishPickingMediaWithInfo:(NSDictionary *)info
{
NSString *mediaType = info[UIImagePickerControllerMediaType];
UIImage * image;
[self dismissViewControllerAnimated:YES completion:nil];
image = info[UIImagePickerControllerOriginalImage];
image = [self imageWithImage:image scaledToSize:CGSizeMake(256, 256)];
self.imageView.image = image;
if (_newMedia)
UIImageWriteToSavedPhotosAlbum(image,
self,
#selector(image:finishedSavingWithError:contextInfo:),
nil);
self.imageView.image = image;
self.imageStay = image
}
In here, the cache variable has the same memory address as image, and my guess is that this later gets deleted.
You shouldn't be worrying about NSCopying, or making a copy of a UIImage, this isn't your problem. You are losing the reference to the image in your imageView. The OS is holding onto the image, if not it would disappear from your imageView. I expect you just aren't referring to it correctly. Perhaps you should show what you mean by a 'caching variable'...
In any case a reliable way to get to the image currently displayed in your imageView is to obtain it from the imageView's image property, as self.imageView.image.
update
If self.imageView.image might be subject to filtering, and you also need to keep a reference to the original image (eg in self.imageStay) you should declare the imageStay property as
#property (nonatomic, strong) UIImage* imageStay;
If you have done this correctly, and still have a nil reference in self.imageStay, step through your code in the debugger to see what is going on here:
self.imageView.image = image;
if (_newMedia)
UIImageWriteToSavedPhotosAlbum(image,
self,
#selector(image:finishedSavingWithError:contextInfo:),
nil);
self.imageView.image = image;
self.imageStay = image
self.imageStay and self.imageView.image should now both have a reference to the same object. By the way, you second assignment of self.imageView.image = image here is redundant.
Then in - (IBAction)samplingSliderChanged:(id)sender you should find those references persisting.
Your problem might be caused if you have declared your #property imageStay as a weak reference. It should be strong to ensure that it persists.
Related
I am having an issue with getting the name of an image that is taken from the photo library or camera. I have successfully picked the image but I want to send the picked image as an email attachment using MFMailComposeViewController. The problem is that the MFMailComposeViewController required a specific name of the image. I have used the following code to get the image name, and it always returning "assest.JPG" and that is not working with MFMailComposeViewController
- (void) imagePickerController:(UIImagePickerController *)picker didFinishPickingImage:(UIImage *)image editingInfo:(NSDictionary *)editingInfo{
[imageView setImage:image];
image_ = image;
image_ = [UIImage imageNamed:#"file.jpg"];
NSURL *imagePath = [editingInfo objectForKey:#"UIImagePickerControllerReferenceURL"];
imageName = [imagePath lastPathComponent];
NSLog(#"%#",imageName);
[self dismissModalViewControllerAnimated:YES];
if (CAMORLIB == 1) {
UIImageWriteToSavedPhotosAlbum(image, self, #selector(image:didFinishSavingWithError:contextInfo:), nil);
}}
is there is any other way to deal with this issue?
Thanks,
Mohammed
Your code here:
image_ = image;
image_ = [UIImage imageNamed:#"file.jpg"];
doesn't make sense, as it assigns to image_ twice. The UIImage method -imageNamed:(NSString*)name tries to load a file named file.jpg from disk.
The reason your MFMailComposeViewController doesn't work is that the above code results in image_ being nil because [UIImage imageNamed:#"filename"] doesn't find a file named that (and as a result returns nil)
Attaching to mail
When attaching an image to a MFMailComposeViewController, you tell it what filename you want the image to have, along with the image's data:
NSData* myData = UIImageJPEGRepresentation(image, .70f);
[picker addAttachmentData:myData mimeType:#"image/jpg" fileName:#"desiredfilename.jpg"];
Here, image is a UIImage and #"desiredfilename.jpg" is the filename you want the image to appear to have in the email.
Pulling an image from the Assets Library only gives you the guid url of the item.
There is no real construct of names in the Assets Library. The photo library has a few properties but they are as follows
ALAssetPropertyType
ALAssetPropertyLocation
ALAssetPropertyDuration
ALAssetPropertyOrientation
ALAssetPropertyDate
all of which can be found in AssetsLibrary.framework\ALAsset.h
to put a name for images I gather from there, I usually put
[NSString stringWithFormat:"Image%#.png",imageNumber]
where image number incremented as I add images.
Hope that helps
I used the codes below to save images to photo librabry
for(int i=0;i<[imageNameArray count];i++)
{
NSMutableString *sss=[[NSMutableString alloc] initWithString: myAppPath];
[sss appendString:#"/"] ;
[sss appendString: [NSString stringWithFormat:#"%#",[imageNameArray objectAtIndex:i] ] ] ;
UIImage *image = [[[UIImage alloc] initWithContentsOfFile:sss]autorelease];
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
[sss release];
}
but sometimes, it can not save images correctly, I found that in photo library, there are one or more black blocks replace the saved images in thumbnail mode, but if I click the black blocks, the original images can display correctly.
Is there anyone met the same problem?
Welcome any comment
Thanks
interdev
It could be a problem that you're saving the images in quick succession, and as a result, the save calls are overlapping. (It takes longer to write an image to the disk than it does to return from the function).
As far as I understand the save image function is asynchronous, and allows a delegate/call back to be registered (in the places where you're passing nil).
You should probably wait until one image has finished writing until you start another.
You can engineer this using the delegate and callbacks. To make it better for the user and to give them an idea of whats happening, you can display a progress bar and other information such as "1/3 images saved" etc.
Edit:
For a better context, I looked up the documentation:
The UIImageWriteToSavedPhotosAlbum function is declared as such:
void UIImageWriteToSavedPhotosAlbum (
UIImage *image,
id completionTarget,
SEL completionSelector,
void *contextInfo
);
you should set the completionTarget (likely to be self) and the completionSelector which should have the following signature:
- (void) image: (UIImage *) image
didFinishSavingWithError: (NSError *) error
contextInfo: (void *) contextInfo;
In the completion selector, you can perform the logic of whether or not you need to perform another save, and update your 'progress' UI.
UIImage *image = [[UIImage alloc] initWithData:data];
((YKSubCategory *)[_subCategories objectAtIndex:connection.tag - 1]).menuImage = image;
[image release];
Here is the code I've written. The data is exists and the designed image is created. (I've tested it with an imageview and the image appears).
The problem comes when I try to set the menuImage property. It will not be set. I don't know why. So the value of the menuImage property remains nil.
Here is the property definition:
#property (nonatomic, retain) UIImage *menuImage;
What can be the problem?
Edit:
I've divided the code for the request of walkytalky.
Here is the code I've written:
UIImage *image = [[UIImage alloc] initWithData:data];
YKSubCategory *subCategory = (YKSubCategory *)[_subCategories objectAtIndex:connection.tag - 1];
subCategory.menuImage = image;
[image release];
[_tableView reloadData];
So now the funny thing is that the subCategory variable is the variable what I expect. Then after I set the menuImage property then the variable for this property is set. I can see in the debugger, but in the array isn't set. What is this?
(i) Have you #synthesize-d menuImage?
(ii) Can you break up the setter line to first get the YKSubCategory object and test it exists and is what you expect?
YKSubCategory* target = (YKSubCategory*)[_subCategories objectAtIndex:connection.tag - 1];
NSLog("subcategory target = %#", target);
// other tests here
target.menuImage = image;
Otherwise, I don't think we have enough info to solve this. What does menuImage look like immediately after setting?
EDIT: when you say "in the array isn't set", do you mean that the actual object in the array differs from the one you've just set right there and then? Or that when you come back to the array when loading the table data the menuImage is no longer set? Or that it just doesn't show in the table?
The first seems impossible on the face of it, so let's think about the second and third.
If menuImage has been reset by the time you come back to it, there must be some code setting it somewhere. This may not go through the property accessor, but for starters you might explicitly implement the setter to log the changes:
- (void) setMenuImage:(UIImage*)newImage
{
if ( newImage != menuImage )
{
NSLog(#"Changing menuImage from %# to %#", menuImage, newImage);
[newImage retain];
[menuImage release];
menuImage = newImage;
}
}
Another possibility is that the actual YKSubCategory object in the array has changed, or even that the arrays are different, so check that the pointers are the same in both locations.
On the other hand, if the menu image ivar is set, but it's not showing up in the table, you need to check your drawing code.
Instruments' Leaks tells me that this UIImage is leaking:
UIImage *image = [[UIImage alloc] initWithContentsOfFile:[imagesPath stringByAppendingPathComponent:[NSString stringWithFormat:#"/%#.png", [postsArrayID objectAtIndex:indexPath.row]]]];
// If image contains anything, set cellImage to image. If image is empty, try one more time or use noImage.png, set in IB
if (image != nil){
// If image != nil, set cellImage to that image
cell.cellImage.image = image;
}
image = nil;
[image release];
(class cell (custom table view cell) also releases cellImage in dealloc method).
I haven't got a clue of why it's leaking, but it certainly is.
The images gets loaded multiple times in a cellForRowAtIndexPath:-method. The first three cells' image does not leak (130px high, all the space avaliable).
Leaks gives me no other info than that a UIImage allocated here in the code leaks.
Can you help me figure it out? Thanks :)
The code you have there would be correct if image was a #property. You can release a #property by doing self.property = nil because of the way the setter works. The setter releases the old object and sets the ivar to the value. In order to fix this you would need to put [image release] first. What is happening here is that you set image to nil and then you are essentially doing [nil release]. The old image is just floating around somewhere. So to fix this do the following:
[image release];
image = nil;
You are setting it to nil before calling release. So you are releasing nil instead of image.
Change from:
image = nil;
[image release];
to:
[image release];
image = nil;
Little bit code modification
UIImage *image = [[UIImage alloc] initWithContentsOfFile:[imagesPath
stringByAppendingPathComponent:[NSString stringWithFormat:#"/%#.png",
[postsArrayID objectAtIndex:indexPath.row]]]];
if (image){
cell.cellImage.image = image;
[image release];
UPDATED Scroll down to see the question re-asked more clearly....
If I had the name of a particular UIImageView (IBOutlet) stored in a variable, how can I use it to change the image that is displayed. I tried this, but it does not work.
I'm still new to iphone programming, so any help would be appreciated.
NSString *TmpImage = #"0.png";
NSString *Tst = #"si1_1_2";
TmpImage = #"1.png";
UIImage *sampleimage = [[UIImage imageNamed:TmpImage] retain];
((UIImageView *) (Tst)).image = sampleimage; // This is the line in question
[sampleimage release];
RESTATED:
I have a bunch of images on the screen.... UIImageView *s1, *s2 ,*s3 etc up to *s10
Now suppose I want to update the image each displays to the same image.
Rather than doing
s1.image = sampleimage;
s2.image = sampleimage;
:
s10.image = sampleimage;
How could i write a for loop to go from 1 to 10 and then use
the loop var as part of the line that updates the image.
Something like this.
for ( i = 1; i <- 10; ++i )
s(i).image = sample; // I know that does not work
Basic question is how do I incorporate the variable as part of the statement to access the image? Don't get hung up on my example. The main question is how to use a variable as part of the access to some element/object.
Bottom Line... If I can build the name of a UIImageView into a NSString object, How can I then use that NSString object to manipulate the UIImageView.
Thanks!
Ugh! Your line in question:
((UIImageView *) (Tst)).image = sampleimage;
is casting a string pointer as a UIImageView pointer - you're basically saying that your pointer to a string is actually a pointer to a UIImageView! It will compile (because the compiler will accept your assertion happily) but will of course crash on running.
You need to declare a variable of type UIImageView. This can then hold whichever view you want to set the image of. So your code could look like the following:
NSString *TmpImage = #"0.png";
UIImageView *myImageView;
If (someCondition == YES) {
myImageView = si1_1_2; //Assuming this is the name of your UIImageView
} else {
myImageView = si1_1_3; //etc
}
UIImage *sampleimage = [UIImage imageNamed:TmpImage]; //no need to retain it
myImageView.image = sampleImage;
Hopefully this makes sense!
Edit: I should add, why are you trying to have multiple UIImageViews? Because a UIImageView's image can be changed at any time (and in fact can hold many), would it not be better to have merely one UIImageView and just change the image in it?