// This is the line which retrieves the current image on the UIView
CGImageRef originalImage = imageView.image.CGImage;
I declare this in a method I use to edit my image which can be called by several buttons.
Is there anyway I can declare this variable globally so it always holds the first image loaded in the UIView? I can then use a copy variable to do all the editing on e.g.
CGImageRef copyImage = originalImage;
I have tried to make it global by declaring it in the header file and instantiate it in the method file however when I try and access the global variable in another method it is as if it hasn't been made global.
Any advice or better solutions would be greatly appreciated.
It is not entirely clear what you are trying to do, but what I would point out is:
the assignment
CGImageRef copyImage = originalImage;
will not create a copy of your image; it will simply give you a local variable pointing to the same image as originalImage; if you modify copyImage, you also modify originalImage;
if you want to make a copy of an CGImage so that you can modify it freely while keeping the original safe, use:
CGImageRef img1 = CGImageCreateCopy(img2);
if you want to save your original image as under point 2, simply add a property to your class and initialize it soon after you have got your CGImage in place (e.g., in viewDidLoad:
-(void) viewDidLoad {
...
self.originalImage = CGImageCreateCopy(imageView.image.CGImage);
...
}
self.originalImage will be your original image, while you will be able to freely modify imageView.image.CGImage.
Don't forget to release the copied image in your dealloc method.
Hope this helps.
Related
I have a custom class named MyImage inherit from UIImage. Now I have a UIImage object, is there any ways to convert it into a MyImage object?
Update
Sorry for being unclear.
I cannot use a category because I need to add new properties to my class.
What I want to accomplish is to just let my MyImage object point to the original UIImage object.
Update 2
I tried something like this:
- (MyImage*)initWithUIImage:(UIImage *)image {
if (self = [super init]) {
self = image;
}
return self;
}
Obvious it does not work.
Also, since UIImage does not have a method named 'initWithImage:(UIImage*)', I cannot write something like
myImage = [[MyImage alloc] initWithImage:uiImage];
I also tried
self = [image copy]
But the return value is still a UIImage object, not a MyImage object.
Update 3
In MyImage, I need to add 3 properties: url, width, and height. Since I am writing a instant messaging app, and when receiving a new image message, I only have its url, width, and height. Then I assign those to a MyImage object, and download the image in the background.
Now given an original UIImage A, I want to create a new MyImage object B, which points to the same image as A, but with those new properties unassigned. Then I manually assign url, width, and height to B.
to #rmaddy, could you tell me how to write the method
[[MyImage alloc] initWithImage:(UIImage*)]
?
One way I can come up is first convert the UIImage object to NSData, then use
[MyImage imageWithData:]
Is there a better way?
I do not necessarily recommend this as a solution, but there is indeed a way to do exactly what you are looking for. It is called ISA Swizzling. Think of it like method swizzling but for Classes instead of methods.
Take a look at the Objective-C runtime's object_setClass [link]
I won't go into it (there are better resources out there), but this is essentially how KVO works. Regardless, it would be helpful if you better described the functionality of your UIImage subclass, so that we could help describe why a custom initializer as rmaddy describes is probably the best solution.
Your MyImage class is a subclass of UIImage, so if you're creating a new instance you need to properly initialize the superclass as well. UIImage doesn't provide a method to do that from another UIImage instance, but you can use a CGImage, and you can get one of those from the existing image. So do this:
- (MyImage*)initWithUIImage:(UIImage *)image {
if (self = [super initWithCGImage:image.CGImage]) {
// put any necessary MyImage-specific stuff here
}
return self;
}
I have a (retained) UIImage property that is being used to hold a user selected image.
This is the code I have at present when the user makes a selection:
- (IBAction) selectImage1 {
UIImage *image = [UIImage imageNamed: #"image1-big.png"];
self.bigImage = image;
}
but I'm wondering if it is possible to omit the use of the temporary variable convenience method and just do this:
- (IBAction) selectImage1 {
self.bigImage = [UIImage imageNamed: #"image1-big.png"];
}
If there are problems with this second method (I'm guessing something to do with memory management), could someone please explain?
Thank you!
The second way is perfectly fine. The line UIImage *image = [UIImage imageNamed: #"image1-big.png"]; gives you a variable image that is auto-released. Assigning it to your ivar via the self.bigImage = image calls bigImage's setter method which retains the value. Thus the line self.bigImage = [UIImage imageNamed: #"image1-big.png"]; is equivalent to the more verbose way.
There is no difference in terms of memory management between the two snippets you posted; unless you get really specific about retain counts in between the two lines in the first snippet.
In an ARC environment, the local variable will be a 'strong' pointer, however it is released when the method leaves scope. In the second snippet, there is no intermediate retain/release'd pointer, and so may actually be slightly more efficient.
The places I have seen the first snippet's technique be necessary are when you have a weak pointer (i.e. a weak #property) where setting self.foo = [UIView ... would immediately allow it to be released. In these cases it is better to use a local variable to keep it around while you work with it:
UIView *someFoo = [UIView...
[self addSubview:someFoo];
self.someWeakProperty = someFoo;
compare with:
self.someWeakProperty = [UIView...
[self addSubview:self.someWeakProperty]; // it's already nil!!
When setting the image for a button, I use stringWithFormat: like so:
[buttonImage setImage:[ImgUtil image:[NSString stringWithFormat:#"myImage_%d.png", selectNum + 1 ]] ];
I want to inspect that string. I thought maybe I could get the name back from the button:
if (buttonImage.image == [UIImage imageNamed:#"myImage_2.png"]) {
NSLog(#"the name of the buttonImage is %#", buttonImage.image);
}
but that doesn't work. How can I look at that string?
You could use associated references to attach a string the key "name" at load time. You create the UIImage from a file, and attach the name using the objective-c associated references API: here.
You can also sub-class UIImage to store an extra name.
You can even add a category to provide an easy API.
If what you want is to "test what the "myImage_%d.png" ends up being" in the following line:
[buttonImage setImage:[ImgUtil image:[NSString stringWithFormat:#"myImage_%d.png", selectNum + 1 ]] ];
Then I would suggest that you reformat and simplify your code. It will give you the additional advantage of making it easier to read:
NSString* imageName = [NSString stringWithFormat:#"myImage_%d.png", selectNum + 1 ];
NSLog(#"imageName is %#", imageName);
[buttonImage setImage:[ImgUtil image:imageName]];
No, you can't.
buttonImage.image is a UIImage stored in memory inside the button.
[UIImage imageNamed:#"myImage_2.png"] creates an entirely different UIImage. Both UIImages could very well have been created from the same file--in this case, #"myImage_2.png"--but they are two separate UIImages in memory.
The == check in your line:
if(buttonImage.image == [UIImage imageNamed:#"myImage_2.png"])
Does not check if the UIImages were created from the same file; it checks if they are pointing to the same location in memory. Which they are not, because they are two separately created and stored UIImage instances.
--
So, no--you cannot do this. Something that might solve your problem another way, though, is to subclass UIButton and add a properly NSString* imageFilename. (If you're setting different images for each control state, you'd need more than one variable to store those image file names in). Then override the setImage:forControlState method of the UIButton subclass and store the filename there every time the image is changed. Then you can perform the following check:
if([imageFileName isEqualToString:[[NSString stringWithFormat:#"myImage_%d.png", selectNum + 1 ]])
And that would get you the answer you want!
You can store the UIImage as instance of the class, and compare it. You won't be using more memory than a pointer.
selectNum stands for the selected image, right?If so, try to get selectNum when picking the picture.
I am having a baffling issue while trying to fill an NSMutableArray with UIImages.
CGBitmapInfo bitmapInfo = kCGBitmapByteOrderMask;
CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
while(...) // <--- Iterates through data sets
{
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, data, numBytes, NULL);
CGImageRef cImage = CGImageCreate(iw, ih, 8, 32, 4 * iw, colorSpace, bitmapInfo, provider, NULL, NO, renderingIntent);
UIImage *finalImage = [UIImage imageWithCGImage:cImage];
[images addObject:finalImage]; // <--- Declared and instantiated earlier
[delegate sendImage:finalImage];
CGImageRelease(cImage);
CGDataProviderRelease(provider);
}
CGColorSpaceRelease(colorSpace);
[delegate operationCompletedWithImages:images];
That is how I have the code running. So I basically have a function running in the while statement that returns the next set of bitmap data, I then create a UIImage and store it into the mutable array.
I've tested this by writing each file to disk and then accessing them which results in the proper set of images. The problem is when using the array to keep data in memory, accessing any image object in the array I get the same exact image over and over. The image is also always the last image of the set.
I've tried testing the contents by setting the array as a UIImageView animation array and by using an NSTimer to cycle the contents. Either way it is just the same image (last image pulled) over and over.
I have this operation running inside a subclassed NSOperation object so it doesn't block the interface. Another interesting aspect here is that when the images array sent with operationCompletedWithImages was giving me the array of duplicate images I tried using the sendImage message and store the images in a different array inside the delegate object (thinking maybe it was a threading issue). This gave me the same results.
I've been stuck on this for over a week with no progress. I've never seen anything like it and I can't find anyone else who has had a similar issue.I would be happy to provide extra information if someone feels it would assist in solving this issue.
If anyone could provide any assistance I would greatly appreciate it.
Not sure if this is the problem, but you should probably be notifying your delegate of the changes on the main thread, not the thread that the NSOperation is running on.
[delegate performSelectorOnMainThread:#selector(sendImage:) withObject:finalImage waitUntilDone:YES];
And likewise for the last message. If the delegate is modifying anything UIKit-related, it must be invoked on the main thread!
One might assume that the NSMutableArray would handle the memory management of the object being added to it, but perhaps because you're dealing with the C level Quartz helpers/ Core wrappers this may not be the case.
In other image manipulating methods I've experimented with they are usually wrapped in an autorelease pool if they are involved with threads.
Have you tried experimenting with release/autorelease on the finalImage?
Figured out the problem. Turned out to be an issue with the backing data pointer.
Where before I was accessing data everytime thinking it would iterate and overwrite with new contents, and the new contents would be plugged into my CGDataProviderRef. This was not the case, I basically ended up supplying a pointer to the same backing data for the Provider. The solution was to copy the data into an NSData object and use that copy for the DataProvider.
So instead of:
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, data, numBytes, NULL);
It would be:
NSData *pxData = [[NSData alloc] initWithBytes:pFrameRGB->data length:numBytes];
CGDataProviderRef provider = CGDataProviderCreateWithCFData((CFDataRef)pxData);
How can I find out what the image name (usually file name?) loaded into a UIImage instance is? In this case, they were all initWithContentsOfFile:.
Once it's loaded using that, you can't. The file name is only used to load the data into memory. After that, it becomes irrelevant.
If you need to maintain the file name, then perhaps you need to make a class with the filename (as an NSString or NSURL, perhaps) and the image data (as an NSImage or NSData object) as ivars. I'm not sure what your application is, so I can't give you any architectural advice at this point.
If you already know what the image names are you can compare your image to another image using image named:
if (bg.image == [UIImage imageNamed:#"field.jpeg"]) {
bg.image = [UIImage imageNamed:#"cave.jpeg"];
} else {
bg.image = [UIImage imageNamed:#"field.jpeg"];
}
Here I use one button to switch between two images.
It's also something you can't just add a category on UIImage to, unfortunately, because it requires state.