From some reason I have to create two UIButtons programmatically rather than in a .xib/.storyboard file, and I want them to be at the bottom of the screen at all times. I figured out that if I want to have the buttons at the exactly same position they would be if they were created in Interface Builder, I have to define their frame like this:
CGRect captureCodeFrame = [[self captureCodeButton] frame];
CGRect captureReceiptFrame = [[self captureReceiptButton] frame];
captureCodeFrame.origin.x = MARGIN;
captureCodeFrame.origin.y = [[UIScreen mainScreen] bounds].size.height - [[UIApplication sharedApplication] statusBarFrame].size.height - [[[self navigationController] navigationBar] frame].size.height - BUTTON_HEIGHT - MARGIN;
captureReceiptFrame.origin.x = [[UIScreen mainScreen] bounds].size.width - BUTTON_WIDTH - MARGIN;
captureReceiptFrame.origin.y = [[UIScreen mainScreen] bounds].size.height - [[UIApplication sharedApplication] statusBarFrame].size.height - [[[self navigationController] navigationBar] frame].size.height - BUTTON_HEIGHT - MARGIN;
[[self captureCodeButton] setFrame:captureCodeFrame];
[[self captureReceiptButton] setFrame:captureReceiptFrame];
In other words, I'm basically taking the height of the whole screen, subtracting the height of status and navigation bar and then the height of these buttons and the space from bottom end of the screen to set the Y
coordinate for both buttons. I'm using similar technique to determine the X
coordinate for the right button. The three macros are defined like this:
#define MARGIN 20
#define BUTTON_HEIGHT 30
#define BUTTON_WIDTH 136
Finally, I have method (void)didRotate:(NSNotification *)notification
that gets called whenever the user rotated the device and I understand that I need to set new frame for the buttons for them to redraw (Or do I? Maybe it's done via some transformation instead?) but no matter what values I try to assign the new frame to, I just can't get it right. My latest attempt looks like this (shortened to only include left landscape mode):
- (void)didRotate:(NSNotification *)notification
{
UIDeviceOrientation deviceOrientation = [[UIDevice currentDevice] orientation];
CGSize newSize = [[self view] bounds].size;
CGRect captureCodeFrame = [[self captureCodeButton] frame];
CGRect captureReceiptFrame = [[self captureReceiptButton] frame];
[CATransaction begin];
[CATransaction setAnimationDuration:0.0];
[CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
[[self previewLayer] setPosition:CGPointMake(0.5 * newSize.width, 0.5 * newSize.height)];
switch (deviceOrientation)
{
case UIDeviceOrientationPortrait:
captureCodeFrame.origin.x = MARGIN;
captureCodeFrame.origin.y = [[UIScreen mainScreen] bounds].size.height - [[UIApplication sharedApplication] statusBarFrame].size.height - [[[self navigationController] navigationBar] frame].size.height - BUTTON_HEIGHT - MARGIN;
captureReceiptFrame.origin.x = [[UIScreen mainScreen] bounds].size.width - BUTTON_WIDTH - MARGIN;
captureReceiptFrame.origin.y = [[UIScreen mainScreen] bounds].size.height - [[UIApplication sharedApplication] statusBarFrame].size.height - [[[self navigationController] navigationBar] frame].size.height - BUTTON_HEIGHT - MARGIN;
[[self captureCodeButton] setFrame:captureCodeFrame];
[[self captureReceiptButton] setFrame:captureReceiptFrame];
[[self view] setTransform:CGAffineTransformMakeRotation(0)];
[[self previewLayer] setAffineTransform:CGAffineTransformMakeRotation(0)];
break;
case UIDeviceOrientationLandscapeLeft:
captureCodeFrame.origin.x = [[UIScreen mainScreen] bounds].size.width - BUTTON_WIDTH - MARGIN;
captureCodeFrame.origin.y = MARGIN;
// TODO: adjust captureReceiptFrame here
[[self captureCodeButton] setFrame:captureCodeFrame];
[[self view] setTransform:CGAffineTransformMakeRotation((CGFloat) (M_PI / 2))];
[[self previewLayer] setAffineTransform:CGAffineTransformMakeRotation((CGFloat) (-M_PI / 2))];
break;
case UIDeviceOrientationLandscapeRight:
// TODO: something should be here
[[self view] setTransform:CGAffineTransformMakeRotation((CGFloat) (-M_PI / 2))];
[[self previewLayer] setAffineTransform:CGAffineTransformMakeRotation((CGFloat) (M_PI / 2))];
break;
default:
break;
}
[CATransaction commit];
}
But no matter what values I try to assign to origin.x
and origin.y
, the button always appears either off-screen or in a completely weird position. Weird, as in, if I put a value of 100
for both properties, the button doesn't appear roughly 100 points away from top left corner of the screen, but in what seems to be a completely random place to me. It seems that I have no idea what happens with the coordinate system when the device rotates and… well… that's where I'd kindly like to ask you to step in and save my application! :-)
PS: status and navigation bars stay in their position from portrait orientation (customer demands, customer gets), that's why they're not included in the new origin calculation.