2

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.

Cellane
  • 511
  • 1
  • 6
  • 16
  • 1
    Are you using autolayout? Might be responsible. – Volker Feb 02 '14 at 09:36
  • Nowhere in this application (I still haven't gotten used to the "new" techniques, that is storyboards and autolayout, no time to read up on them and they're not recommended to use by the company I work for as other programmers are in the same situation – no time to read up on new stuff), plus this view controller obviously doesn't have a .xib attached to it, it's mostly just camera output (in `AVCaptureVideoPreviewLayer` layer) and these two buttons. – Cellane Feb 02 '14 at 09:41
  • Plus I'm not even sure if you can use autolayout if you lack the .xib and create everything programmatically? No idea :o – Cellane Feb 02 '14 at 09:58
  • possible duplicate of [UIView autoresizingMask - Interface Builder to Code - Programmatically create struts and springs](http://stackoverflow.com/questions/10468389/uiview-autoresizingmask-interface-builder-to-code-programmatically-create-st) – Abizern Apr 07 '14 at 07:50

1 Answers1

0

You need to set proper UIViewAutoresizingMask to your button See this answer

Community
  • 1
  • 1
Timur Mustafaev
  • 4,869
  • 9
  • 63
  • 109
  • I see, well am I right in thinking that the proper mask would be `UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleRightMargin` for the left button since I want the button to expand in width and stay at bottom left corner of the screen? It somehow sort-of works but now the button is definitely more than 20 points away from the bottom of the screen in portrait mode. I wonder if constraints could solve that… – Cellane Feb 02 '14 at 15:36
  • If the answer is available in another question, it's better to just mark this question as a duplicate. – Abizern Apr 07 '14 at 07:50