How do I override the Points to Pixels iOS specificity and have my image drawn at the right size? - objective-c

I have a 64px by 64px redSquare.png file at a 326ppi resolution. I'm drawing it at the top left corner of my View Controller's window as follows:
myImage = [UIImage imageNamed:#"redSquare.png"];
myImageView = [[UIImageView alloc] initWithImage:myImage];
[self.view addSubview:myImageView];
Given that the iPhone 4S has a screen resolution of 960x640 (326ppi) there should be enough room for 9 more squares to fit next to the first one. However there's only room for 4 more. i.e. the square is drawn larger than what it should given my measurements.
// even tried resizing UIImageView in case it was
// resizing my image to a different size, by adding
// this next line, but no success there either :
myImageView.frame = CGRectMake(0, 0, 64, 64);
I believe it has to do with the way the device is "translating" my pixels. I read about the distinction between Points Versus Pixels in Apple's documentation but it doesn't mention how one can work around this problem. I know I'm measuring in pixels. Should I be measuring in points? And how could I do that? How exactly am I to resize my image so that it can hold 9 more same-sized squares next to it (i.e. on the same horizontal..) ?
Thank you

To display an image at full resolution on a Retina display, it needs to have #2x appended to the end of its name. In practice, this means you should save the image you're currently using as redSquare#2x.png and a version of that image in 32x32 pixels as redSquare.png.
Once you have done this, there is no need to change your code. The appropriate image will be displayed depending on the device's capabilities. This will allow your app to render correctly on both Retina and non-Retina devices.

Related

How to find back the real position on the image on iOS?

Here is the view I got, I got a layer view, detect user touch, and a image view, which showing the image. The layer view is cover on top of the image view. The image view's image is aspect fit. So, it won't lost the ratio. If in my layer view touch on 100, 240, it is a layer view coordinate, but not the image's coordinate. I would like to know how to convert the layer view's coordinate to a image's coordinate. In this example, the image size may be 180*180, so, the coordinate in layer view in the image is 60, 90.
Thanks.
If I'm understanding this question correctly, you want to take a point, which is currently in relation to the layer's coordinate system, and convert it to the image view's coordinate system?
In that case, there are a couple of ways to do this.
Easiest is to use convertPoint:fromView: or convertPoint:toView:
CGPoint imageViewTouchPoint = [layerView convertPoint:touchPoint fromView:imageView];
CGPoint imageViewTouchPoint = [imageView convertPoint:touchPoint toView:layerView];
Either one should work.
EDIT - I realize now that this is only if the UIImageView has the same frame as the UIImage, which you said it might not, due to the UIViewContentModeScaleAspectFit property.
In this case, unless I'm mistaken, the image frame is calculated inside the UIImageView drawRect: method and isn't a property that gets set. This means you'll have to calculate this on your own.
Definitely get the imageViewTouchPoint from one of the methods above (just in case you want to use the same logic on a UIImageView which isn't the full screen size).
You will then need to calculate the scaled image frame. There are a couple of ways to do this. Some people go brute force and manually calculate based on which side of the image is longer, then determining which side should be scaled. Then they calculate the origin by by centering the image and subtracting the image and image view's sides and dividing by two.
I like to write as little code as possible if it's unnecessary, even if it means importing a framework. If you import AVFoundation you get a method AVMakeRectWithAspectRatioInsideRect which you can use to actually calculate the scaled rectangle in one line of code.
CGRect imageRect = AVMakeRectWithAspectRatioInsideRect(image.size, imageView.frame);
Whichever method you use, you will then simply translate your touched point with the scaled image origin:
CGPoint imageTouchPoint = CGPointMake(imageViewTouchPoint.x - imageRect.origin.x, imageViewTouchPoint.y - imageRect.origin.y);
You have to do the math yourself. Calculate the aspect ratio of your image and compare with the aspect ratio of the image view's bounds.
Look at this question: How to Get Image position in ImageView
After searching more, got a hack:
CGSize imageInViewSize = [photo resizedImageWithContentMode:UIViewContentModeScaleAspectFit bounds:imageView.size interpolationQuality:kCGInterpolationNone].size;
CGRect overlayRect = CGRectMake((imageView.frame.size.width - imageInViewSize.width) / 2,
(imageView.frame.size.height - imageInViewSize.height) / 2,
imageInViewSize.width,
imageInViewSize.height);
NSLog(#"Frame of Image inside UIImageView: Left:%f Top:%f Width:%f Height:%f \n", overlayRect.origin.x, overlayRect.origin.y, overlayRect.size.width, overlayRect.size.height);

Core Animation Bar

Currently I have an image... A battery Icon...
_MeterBar = [[UIImageView alloc] initWithFrame:CGRectMake(25, 40, 50, 40)];
_MeterBar.image = [UIImage imageNamed:#"battery-empty-icon.png"];
_MeterBar.backgroundColor = [UIColor clearColor];
I am attempting to add an animation that shows the battery loading to a specific point of the battery when a value is fetched from a web server.
Fetching the value is no problem but adding the animation is an issue. I basically copied and pasted some code a user answered before but I am stuck :/. When I add the animation, there is no image.....
CABasicAnimation *scaleToValue = [CABasicAnimation animationWithKeyPath:#"transform.scale.x"];
scaleToValue.toValue = [NSNumber numberWithFloat:100.0f];
scaleToValue.fromValue = [NSNumber numberWithFloat:0];
scaleToValue.duration = 1.0f;
scaleToValue.delegate = self;
_MeterBar.layer.anchorPoint = CGPointMake(0.5, 1);
[_MeterBar.layer addAnimation:scaleToValue forKey:#"scaleRight"];
CGAffineTransform scaleTo = CGAffineTransformMakeScale(1.0f, 50.0f);
_MeterBar.transform = scaleTo;
[_PowerNowView addSubview:_MeterBar];
The battery icon is more wide than tall so I'm animating from 0 -> a percentage on the x axis.
If anyone has any hints, tips, or sample examples that does this exact animation, I would greatly appreciate your help :)
Thank you.
P.S. Heres the URL of the battery Icon i found : Battery Icon
Sorry for not being clear but I would like to have a color layer seperate of the battery icon...
I would like to create a bar over the battery image and have the bar dynamically animate with respect to the size of the battery.... not actually resize the battery image itself....
So there would be two layers, one for the battery that is mainly static in size and the other layer being the colored BAR, maybe a green color to represent the amount of the BATTERY image that is filled.
A good reference of what I would like to do is what the MINT application does, whose purpose is to record your bank statements.
The final animated battery above is very nice but I would just like to show a bar within the battery and animate it with respect to the pulled data value.
A couple of things.
Your scale of 100 would make your layer 100 times larger than normal. If it's 300 pixels on a side, a scale of 100 would display it as 30,000 pixels on a side.
You want your to scale value to 1.0, not 100.
Also, I'm not sure if scaling the transform multiplies the previous scale by a growing factor. If it does, then a starting scale value of 0 would prevent it from ever growing, since anything times zero is still zero. Try a from value of .0001 instead of 0.0,
If you want your battery image to grow from left to right, wouldn't you want your anchor point to be (0,.5) rather than (.5, 1)?
And, when you change the anchor point, it not only changes the center for transforms, but it moves the position of the image. You will need to adjust the position to compensate. I always have to scratch my head and think about it really hard (and usually get it wrong anyway) but I think you would want to shift the X position of your view to the right by half of it's width.
Note that scaling your image will cause it to stretch oddly. It will start out short and fat and stretch out to normal size.
It might be better to attach a CAShapeLayer to the view as a mask layer, and animate the shape layer's path from an empty rectangle (Width = 0, height = full height of view) to a rectangle that is the full size of the view. That would cause the view to animate in a "wipe" animation where it is revealed from left to right without stretching.

Managing retina UIButton image sizes

I am a bit confused on managing the different sized images with retina and non-retina displays.
I add a custom button in Storyboard and add some text and then add the backgroundimage, which is a vector done in Illustrator (Width: 630 / Height:130):
UIImage *img = [UIImage imageNamed:#"iPad1_orange_button.png"];
[myButton setBackgroundImage:img forState:UIControlStateNormal];
the button shows up:
...the button comes out very small.
I have another image with the #2x for retina but that comes out the same size.
My question is how to manage the sizes of the buttons in regards to image size. DO i need to set the size of the button manually?
Also, when i create a button in Illustrator with the same pixel size as the button i use in XCODE and export it as .png, add it to XCODE and drag it into Storyboard it comes out very large.
Just a quick clarification:
The storyboard dimensions are NOT in pixels.
An iPhone 4S has 640x960(x,y) # 326ppi. xCode dimensions are 320by460(x,y). Just take the numbers and convert em to get the appropriate pixel sizes.
IE: If you want a button that that is 100 wide and 100 tall in storyboard you'd to create an image 100*(640/320) px wide and 100*(960/460) px tall (I believe my math is right...).
Depending on what you're building for you'd need different images to cater to the different devices.
On another note, the term retina display doesn't designate a clear-cut standard of px by px. If I recall correctly it's a term Apple coined that basically means that a screen has enough px that the human eye (hence retina) would not be able to see the jaggies.
Similar to Intel's coining of the term Ultrabook; no REAL spec floor/ceiling just a marketing ploy.
Think of dimensions and what you would consider a pixel to be a "density independent pixel". You are always working in the original size when it comes to position and size but it is up to the operating system to handle the different resolutions for you. For now we have the original size and Retina which is X2 the density but this could change. You never need to manually change the resolution for the given device.
This is simple:
make the button frame the size of the normal res image
when you load the image with UIImage imageNamed: do it like this
UIImage *img = [UIImage imageNamed:#"iPad1_orange_button"];
*Note I removed the file extension (.png) - this instructs UIImage to select the resolution appropriate version, the regular or the #2x. iOS will show the 2x version of the image in the same frame size as the normal res image. Pretty simple.

Images in NSButton and NSImageView Blurred

I am completely stumped here; I have a series of small images I'm tinkering with and making into buttons:
And as you can see they are all decently crisp and sharp, and retain this when I open the png files in Preview and what not.
However, when I use them in NSButtons and NSImageViews in Interface Builder, setting Scaling to None:
The images become horribly blurred. What am I doing wrong? I don't know where to start and what to try; should I go back to the icons and try to make them pixel perfect? Does it have to do with anti-aliasing or something along those lines?
EDIT:
For some reason, it seems as if the NSButtons and NSImageViews are loading the high resolution versions of the images, even though I'm on a normal display, which can be identified by a slight light blue stroke I added to them. For some reason, Quartz Debug does not identify these as high resolution images and there's no red tint. Removing references to the #2x images does fix the problem... but...
If you check out session 245 in the WWDC 2012 videos Advanced Tips and Tricks for High Resolution on OS X in the first section on NSImage you'll find out why.
NSImage doesn't have any concept of high resolution - it just uses the smallest image that has more pixels than the space it has to fill - so if your NSImageView is bigger in dimension than your 1x image it will use the 2x image as it has more pixels.
I have this problem before. It seems that if your image's DPI isn't 72, the image size will be wrong. You can get the real size use the code below.
NSImage *image = [NSImage imageNamed:#"image"];
NSBitmapImageRep *rep = [NSBitmapImageRep imageRepWithData:[image TIFFRepresentation]];
NSSize size = NSMakeSize([rep pixelsWide], [rep pixelsHigh]);
[image setSize: size];
When specifying image names in Interface Builder and [NSImage imageNamed:], make sure to use foo instead of foo.png. While iOS is smart enough to add the #2x in the later case, Mac OS X is not. It will load the non-retina image in the later case, but will add the #2x in the first case (if such an image is present).
Are you assigning the images to your Buttons in IB or in Code?
If you are doing it in code, maybe creating a copy of the image (e.g. [myImage copy]), and assigning that copy to your button may solve this.
In my case (drawing icons in custom NSOutlineView), I had to make sure that the x,y origin of the drawRect is rounded to int values:
NSMakeRect( round(NSMinX(cellFrame)-iconSize.width),
round(NSMidY(cellFrame)-(iconSize.height/2.0f)), …);
This is actually a response to the earlier post about DPI, but I was unable to reply directly to it. The code in that post gave the true pixel dimensions for me (that is, it did not indicate any trouble). However, image DPI was definitely the culprit in my case. The symptoms I was seeing were:
With my NSImageViews set to No Scaling, the images would appear squashed.
With my NSImageViews set to Axes Independently, most images would appear correctly if the dimensions of the NSImageViews were set to exactly match the dimensions of the image.
However, even in this case, some images had strange artifacts in them that were not there when viewing the same image via Preview or elsewhere (or even via Interface Builder, for that matter -- they only appeared at runtime).
The images that had trouble were at a DPI other than 72. When I re-created the images at 72 DPI, all of the above behavior disappeared.
This was a pretty confounding issue -- I hope this helps someone!
For me, I just needed to set image scaling to none:
In Interface Builder
In code
NSImageCell *image;
[image setImageScaling:NSImageScaleNone];
NSButtonCell *button;
[button setImageScaling:NSImageScaleNone];

Scaling an image from the iPhone - iOS App

I let the user select a photo from the iPhone library, and I grab the UIImage.
I output the size of the image, and it says 320x480, but it doesn't seem to be, because when I draw the image on the screen using CGRectMake(0,0,320,480), it only shows the upper left portion of the image. Aren't the images much bigger than 320x480 because of the high resolution?
I'd like to scale the image to force it to be 320x480. If it is less than 320x480, it should not be rescaled at all. If the width is greater than 320 or the height is greater than 480, it should scale in a way so that it becomes as close to 320x480 as possible, but by keeping the proper proportion of width to height. So, for instance, if it scales to 320x420, that is fine, or 280x480.
How can I do this in Objective-C?
Setting the image view's content mode like this:
myView.contentMode = UIViewContentModeScaleAspectFit;
will preserve the aspect ratio.