I have a ViewController to which I applied the retina 3.5" form factor in the story board. The iOS iPhone 6.1 simulator also has the Retina configured.
When I try to position a UIImageView using SetFrame, its CGRect coordinates are in the non-retina form (i.e. when I position to 320x480, it goes to the bottom right instead of the middle of the screen):
[myImageView setFrame:CGRectMake(320, 480, myImageView.frame.size.width, myImageView.frame.size.height)];
[self.view addSubview:myImageView];
How to have CGRect coordinates for Retina when using SetFrame ?
Thanks.
The reason for this is because iOS uses points instead of pixels. This way, the same code will work on a retina and a non-retina screen. Therefore, when you set the location to (320,480) you are setting it to point (320,480) not pixel (320,480). This way, if the phone is non-retina, that point will end up being pixel (320, 480) and on retina, it will end up being pixel (640,960).
So what it looks like you want is:
[myImageView setFrame:CGRectMake(160, 240, myImageView.frame.size.width, myImageView.frame.size.height)];
[self.view addSubview:myImageView];
which will place the imageView's top-left corner in the same location on both retina and normal display.
To center a view:
CGFloat x = self.view.frame.size.width;
CGFloat h = self.view.frame.size.height;
[myImageView setCenter:CGPointMake(w/2, h/2)];
...
[self.view addSubview:myImageView];
CGRectMake needs a x,y,width,height.. X,y are the topleft of the view, so use 0,0,w,h for full frame views.
Related
I am working on an ObjectiveC app in which I need to set the width and height of a uiview in pixels and than scale that uiview equal to the devices width. The width and height values in pixels are fixed ( let say 900 px x 500 px).
Currently I am doing this,
UIScreen* mainScreen = [UIScreen mainScreen];
CGRect frame = CGRectMake(0, 0, 900.0, 500.0);
[view setFrame:frame];
CGFloat valScale = mainScreen.bounds.size.width/900.0;
[view setContentScaleFactor:valScale];
But this is not giving me the desired values.
What should I do?
(P.S) Does view.frame.size.width return the width in pixels or points?
Hi for scaling a view you can use the transform property. Say you wish to scale your UIView by a factor of 2 then you can use.
self.view.transform = CGAffineTransformScale(CGAffineTransformIdentity, 2, 2);
CGRect width and height returns points not pixels. One point has two pixels for 2x devices like iPhone 6,7 and three pixels for 3x devices like iPhone 6 Plus,7 Plus.
For more detail you can refer this Apple's documentation link.
I know we are operating on points not pixels and in most cases it's convenient, but I need to make UIView be 1 pixel instead of 2 pixel height. So, if you drag and drop some UIView (separator line) in Interaface builder, and make it the height of 1px (point) then it will still look like 2 pixel size line on retina screen (both on device and simulator).
I know there contentScaleFactor property on the view which show is it retina (2.0f) or not (1.0f). It looks like the views has the value of 1.0f, so you need to retrieve that from main screen:
[UIScreen mainScreen].scale;
This returns me 2.0f. Now, I'v added height constraint for this separator view added the method which checks isRetina and divides the line to make it exactly 1 pixel:
- (void)awakeFromNib{
[super awakeFromNib];
CGFloat isRetina = ([UIScreen mainScreen].scale == 2.0f) ? YES : NO;
if (isRetina) {
self.separatorViewHeightConstraint.constant /= 2;
}
}
This works, I'm just not sure is it good idea to use 0.5 value ...
To support newer 3x displays (e.g. iPhone 6+) use this code:
UIScreen* mainScreen = [UIScreen mainScreen];
CGFloat onePixel = 1.0 / mainScreen.scale;
if ([mainScreen respondsToSelector:#selector(nativeScale)])
onePixel = 1.0 / mainScreen.nativeScale;
Your code is valid. Using 0.5 to set the frame of a UIView will work as desired, as the frame's arguments are CGFloat's. If you wish to use a CGFloat representing a single pixel in point units for something other than self.separatorViewHeightConstraint.constant, the code below will work.
CGFloat scaleOfMainScreen = [UIScreen mainScreen].scale;
CGFloat alwaysOnePixelInPointUnits = 1.0/scaleOfMainScreen;
You could just do
self.separatorViewHeightConstraint.constant = self.separatorViewHeightConstraint.constant / [UIScreen mainScreen].scale;
yes setting the value to 0.5 is the only way to get "real" 1px lines on retina
Sadly none of the other answers apply for iPhone 6 Plus.
1px lines are not possible on iPhone 6 Plus, as the screen is rendered in 1242x2208 and then down sampled to 1080x1920. Sometimes you will get an almost perfect 1px line, and sometimes the line will disappear completely.
See http://www.paintcodeapp.com/news/iphone-6-screens-demystified for a proper explanation.
I'm having a nightmare time trying to correct a photo taken with AVFoundation captureStillImageAsynchronouslyFromConnection to size and orient to exactly what is shown on the screen.
I show the AVCaptureVideoPreviewLayer with this code to make sure it displays the correct way up at all rotations:
previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.captureSession];
[previewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
previewLayer.frame = CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height);
if ([[previewLayer connection] isVideoOrientationSupported])
{
[[previewLayer connection] setVideoOrientation:(AVCaptureVideoOrientation)[UIApplication sharedApplication].statusBarOrientation];
}
[self.view.layer insertSublayer:previewLayer atIndex:0];
Now when I have a returned image it needs cropping as it's much bigger than what was displayed.
I know there are loads of UIImage cropping examples, but the first hurdle I seem to have is finding the correct CGRect to use. When I simply crop to self.view.frame the image is cropped at the wrong location.
The preview is using AVLayerVideoGravityResizeAspectFill and I have my UIImageView also set to AspectFill
So how can I get the correct frame that AVFoundation is displaying on screen from the preview layer?
EDIT ----
Here's an example of the problem i'm facing. Using the front camera of an iPad Mini, the camera using the resolution 720x1280 but the display is 768x0124. The view displays this (See the dado rail at the top of the image:
Then when I take the image and display it, it looks like this:
Obviously the camera display was centred in the view, but the cropped image is taken from the top(none seen) section of the photo.
I'm working on a similar project right now and thought I might be able to help, if you haven't already figured this out.
the first hurdle I seem to have is finding the correct CGRect to use. When I simply crop to self.view.frame the image is cropped at the wrong location.
Let's say your image is 720x1280 and you want your image to be cropped to the rectangle of your display, which is a CGRect of size 768x1024. You can't just pass a rectangle of size 768x1024. First, your image isn't 768 pixels wide. Second, you need to specify the placement of that rectangle with respects to the image (i.e. by specifying the rectangle's origin point). In your example, self.view.frame is a CGRect that has an origin of (0, 0). That's why it's always cropping from the top of your image rather than from the center.
Calculating the cropping rectangle is a bit tricky because you have a few different coordinate systems.
You've got your view controller's view, which has...
...a video preview layer as a sublayer, which is displaying an aspect-filled image, but...
...the AVCaptureOutput returns a UIImage that not only has a different width/height than the video preview, but it also has a different aspect ratio.
So because your preview layer is displaying a centered and cropped preview image (i.e. aspect fill), what you basically want to find is the CGRect that:
Has the same display ratio as self.view.bounds
Has the same smaller dimension size as the smaller dimension of the UIImage (i.e. aspect fit)
Is centered in the UIImage
So something like this:
// Determine the width:height ratio of the crop rect, based on self.bounds
CGFloat widthToHeightRatio = self.bounds.size.width / self.bounds.size.height;
CGRect cropRect;
// Set the crop rect's smaller dimension to match the image's smaller dimension, and
// scale its other dimension according to the width:height ratio.
if (image.size.width < image.size.height) {
cropRect.size.width = image.size.width;
cropRect.size.height = cropRect.size.width / widthToHeightRatio;
} else {
cropRect.size.width = image.size.height * widthToHeightRatio;
cropRect.size.height = image.size.height;
}
// Center the rect in the longer dimension
if (cropRect.size.width < cropRect.size.height) {
cropRect.origin.x = 0;
cropRect.origin.y = (image.size.height - cropRect.size.height) / 2.0;
} else {
cropRect.origin.x = (image.size.width - cropRect.size.width) / 2.0;
cropRect.origin.y = 0;
}
So finally, to go back to your original example where the image is 720x1280, and you want your image to be cropped to the rectangle of your display which is 768x1024, you will end up with a CGRect of size 720x960, with an origin of x = 0, y = 1280-960/2 = 160.
I have a UIScrollView which contains a UIImageView
I need to zoom to a specific location relative to the UIImage and NOT the UIScrollView
So for example I have a UIImage where the size is 1000,1000 PX
I would like to zoom to a square such that CGRect = (400,500,100,100) inside that image
Unfortunately self.scrollView zoomToRect:animated isn't working properly because this rect is outside of its view and not in the same coordinate system
Also the aspect ratio of the UIImage can change inside of the UIImageview so it can be difficult to calculate its offset inside of the UIImageView (the black bars at the top and bottom of the image)
I know about
convertRect:fromView:
so thought about doing
-(void)zoomToRectInImage:(CGRect)rect
{
CGRect rect1 = [self.imageView.image convertRect:rect fromView:self.imageView.image]; // error because UIImage isn't a view
CGRect scrollViewRect = [self.scrollView convertRect:rect1 fromView:self.imageView];
[self.scrollView zoomToRect:scrollViewRect animated:YES]
}
I asked the question in a different format and this might help out someone
UIScrollView how to zoomToRect in UIImageView
This is probably linked to another unsolved mystery of mine.
I'm drawing Orthographic 2d on iPhone, using real device and simulator. I'm trying to color my pixels a given color depending on how far they are from arbitrary point in pixelspace of 'A', which I pass in (hard code). I'm doing everything in Retina 960x640 resolution. I calculate distance from A to gl_FragCoord, and I color based on leaping between 2 colors with the 'max' being 300px distance.
When on simulator (with retina display) I need to give a center point of "460" pixels for screen midpoint X.. Y I give 160px, and I look for distance of '300'px.. to get the same effect on device I need center of 960X and distance of 150 to get the same results (interestingly, px 80 doesn't give the same results I want but 160 could be an overshoot on the original...)
Obviously a retina issue is at play. But where, and how, and how do I find and fix it?
I'm using:
glViewport(0, 0, 960.0f, 640.0f);
and:
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &framebufferWidth);
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &framebufferHeight);
And:
[self setView:[[EAGLView alloc] initWithFrame:[UIScreen mainScreen].bounds]];
[(EAGLView *)[self view] setContentScaleFactor:2.0f];
You shouldn't hardcode "glViewport(0, 0, 960.0f, 640.0f);", setup the viewport this way:
glViewport(0, 0, framebufferWidth, framebufferHeight);
Also don't hardcode the content scale, you can findout the content scale with:
float contentScale = 1.0f;
if ([[UIScreen mainScreen] respondsToSelector:#selector(displayLinkWithTarget:selector:)]) {
contentScale = [[UIScreen mainScreen] scale];
}
About the pixel distance, since you want the distance to be the double with retina display, you can add an uniform to your shader with the content scale.
Now iOS devices can have multiple different screens, and a UIWindow can be placed in a non-main screen. In that case, you can use self.view.window.screen.scale to get current screen's scale dynamically.