How to get suitable CGImage from combined TIFF for display scale - objective-c

I have two PNGs in a Mac project. Normal and #2x. Xcode combines these into a single TIFF with the #2x being at index 0 and the #1x at index 1.
What is the suggested approach to get the appropriate image as CGImageRef version (for use with Quartz) for the current display scale?
I can get the image manually via CGImageSource:
NSBundle *mainBundle = [NSBundle mainBundle];
NSURL *URL = [mainBundle URLForResource:#"Canvas-Bkgd-Tile" withExtension:#"tiff"];
CGImageSourceRef source = CGImageSourceCreateWithURL((__bridge CFURLRef)(URL), NULL);
_patternImage = CGImageSourceCreateImageAtIndex(source, 1, NULL); // index 1 is #1x, index 0 is #2x
CFRelease(source);
I also found this to be working, but I am not certain that this will return the Retina version on a Retina display:
NSImage *patternImage = [NSImage imageNamed:#"Canvas-Bkgd-Tile.tiff"];
_patternImage = [patternImage CGImageForProposedRect:NULL context:nil hints:nil];
CGImageRetain(_patternImage); // retain image, because NSImage goes away
An acceptable answer to this question either provides a solution how to get the CGImage suitable from a combined multi-resolution TIFF, or explains why the second approach here is working. Or what changes are required.

I am opting to answer on "why the second approach here is working".
In one of the WWDC videos published since 2010, they said that :
+[NSImage imageNamed:] chooses the best image representation object available for the current display.
So chances are that you are calling this class method from within a locked focus context (e.g. within a drawRect: method or similar), or maybe you actually called lockFocus yourself. Anyway, the result is that you get the most suitable image. But only when calling +[NSImage imageNamed:].
EDIT: Found it here:
http://adcdownload.apple.com//wwdc_2012/wwdc_2012_session_pdfs/session_213__introduction_to_high_resolution_on_os_x.pdf
Search for the keyword "best" in the slides: "NSImage automatically chooses best representation […]".
So, your second version will return the Retina version on a Retina display, you can be certain of it, it is advertised in the documentation[*].
[*] This will only work if you provide valid artwork.

Related

How can I change the value of an NSImage?

I am a beginning Objective-C programming programmer, and I want to change the value of an NSImage. That means that if I have an NSImage that is a NSStatusUnavaliable, how could I make it a NSStatusAvaliable? Thanks!
So I believe what you're asking is: Somewhere you have an NSImage instance that was initialized like:
NSImage *image = [NSImage imageNamed: #"NSStatusUnavailable"];
And you would like to use the image that is mapped to #"NSStatusAvailable".
Without going into details, you really want to assign your image to [NSImage imageNamed: #"NSStatusAvailable"];
Take a look at the docs for NSImage. Specifically what you might find useful is the imageNamed: class method.
Discussion
This method searches for named images in several places, returning the first image it finds matching the given name. The order of the search is as follows:
Search for an object whose name was set explicitly using the setName: method and currently >resides in the image cache.
Search the app's main bundle for a file whose name matches the specified string. (For information on how the bundle is searched, see ““Accessing a Bundle's Contents”“ in >Bundle Programming Guide.)
Search the Application Kit framework for a shared image with the specified name.
But also since you're new, just reviewing Objective-C docs on object life cycle will be very useful to understand what's going on.
Swift:
let 🈯️ = NSImage(named: .statusAvailable)
let 🅿️ = NSImage(named: .statusPartiallyAvailable)
let 🔴 = NSImage(named: .statusUnavailable)
let 🔶 = NSImage(named: .statusNone)

Why does nil-checking a table view cell property cause one of my cells to copy another?

My table view loads pictures through SDWebImage (async image downloading/cache category for UIImageView) in each tableView:cellForIndexPath:.
// 'spot' is a dictionary of this cell's attributes
NSURL* imageURL = [NSURL URLWithString:(NSString*)[spot objectForKey:#"Image"]];
UIImage* placeholderImage = [UIImage imageNamed:#"placeholder.png"];
^ Loads the images, but is constantly having to make requests or pull from the cache. So I wanted to see if the rendered cells were stored in memory, so I could only make requests for images when I need them. I went about this by adding a simple check for the image property of the image view.
if (!cell.pictureView.image)
{
NSURL* imageURL = [NSURL URLWithString:(NSString*)[spot objectForKey:#"Image"]];
UIImage* placeholderImage = [UIImage imageNamed:#"placeholder.png"];
[cell.pictureView setImageWithURL:imageURL placeholderImage:placeholderImage options:SDWebImageCacheMemoryOnly];
}
This works and keeps my images from having to reload after exiting and reentering the application. But in one of my cells that has a purposely bad image url (for debugging), the image- which used to remain as the imageNamed:#"placeholder"- is now the same image as my second cell.
I'm curious what kind of cell-reuse/cache magic is causing the cell to take the picture of a previous cell.
What is the best way to retain cell properties and only set them when needed?
It's very likely the issue is with the image package you are using. If you are making repeated URL requests to grab images, it is probably best practice to implement NSURLCache, which is a fairly simple and handles caching perfectly so it is not an issue. This is the NSHipster guide on how to use it. Other solutions tend towards these sorts of issues. Since it is a category on UIImageView, it could be any number of things causing issues with UIImageView's implementation when getting used in the cell.

Is there a way to specify the image format when using UIActivityViewController on a UIImage?

It seems that the OS chooses whether to use a PNG or JPEG for reasons unknown. In my case, I want a JPEG sent (via mail, twitter, etc.) but I tend to get a very large PNG.
If I subclass UIActivityItemProvider and return an NSURL pointing to a file that I created myself by writing a JPEG to disk using UIImageJPEGRepresentation, I can of course get a JPEG. However using an NSURL causes other issues (one of them being the inability to use iMessage as a target). Is there any way to use a UIImage, but specify that you would prefer a certain file format? (I'm quite sure the answer is NO, but want to check.)
Actually it just worked to pass in the NSData of the JPEG representation as the activity item.
NSData *data = UIImageJPEGRepresentation(image, 0.6);
UIActivityViewController *activityController = [[UIActivityViewController alloc] initWithActivityItems:#[data]];
applicationActivities:nil];
Looks like the answer is indeed NO.

UIImage causing memory leaks

Instruments is telling me that alot of memory is being allocated when I rapidly set the image name of a UIImageview in my app. I have a UIImageView that changes its image name every frame in my game. When profiled with zombie checking in instruments, the app seems to be constantly gaining live bytes at an enourmous rate. Is there a way that I can deallocate the UIImageView's current image to stop it from doing this? I am using ARC.
My code to assign the UIImageView's image is as follows:
aPlanet.image = [UIImage imageNamed:tempPlanetName];
Where aPlanet is the UIImageView and tempPlanetName is the name of the image. This is called every frame.
[UIImage ImageNamed:] method loads the image into image view and adds this newly created uiimage object to autorelease pool. To get rid of this problem you should use -
NSString *imgPath = [NSBundle mainbundle] pathForResource:#"imageName" ofType:#"png"];
aPlanet.image = [[UIImage alloc] ]initWithContentsOfFile:imgPath];
if you are using arc then you don't need to bother about releasing this newly allocated object of uiimage which was created using initWithContentsOfFile: method.
When you use UIImage imageNamed: it will load and cache that image file. This is intended for reuse of icons and other image resources that will be utilized more than once in your application.
Apart from it seeming somewhat unusual to update an image view with a new image every frame, you should look into alternative means of loading images that you will not need more than once - or even if you do, when you need more control over its lifecycle.
For example have a look at UIImage imageWithContentsOfFile: (documented here: Apple Developer Library Reference). It explicitly states that this method will not do any caching of the image contents.
I hope this helps you, but for every frame I doubt that your performance will be good enough with this approach, but this is probably the topic of a different question should the need arise.

objective-c memory friendly way for background image

i have an ipad app (>30 views / pages) each view has a unique background.
the problem:
whats the best way to set the background (memory friendly)
is there a better way than adding: uiimageview "backgroundView" as a subview?
version1:
[[UIImage alloc] initWithData:imageData];
which seems to be problematic with the retina switch
version2:
self.layer.contents = (id)image.CGImage;
version 3:
UIImage* image = [UIImage imageWithContentsOfFile:fileLocation];
version 2 seems to work fine. maybe someone tell me whats the best approach, and why ;)
thank you
Alex
CGImage is problematic with retina ... version3. is best for memory friendly !
In version 2, you generate a new image object which you have to release manually if you don't use ARC. Version 3 uses an autoreleased object.
Both versions are equal in memory-friendlyness.
I'd prefer version 3 because you don't have to do anything yourself to free the memory.
You could also use [UIImage imageNamed:#"image-name.png"], which also generates an autoreleased object.
If you want it as memory-friendly as possible, you should consider using PVR images, as those are natively supported by the graphics hardware.
Best,
Flo