4

I am using a a translucent Navigation Bar and Status Bar and my View Controller wants full screen. Thus, my View Controller's View extends under the Nav and Status bars and takes the full size of the screen.

I also have a Label which I would like to align directly under the Navigation Bar. Because I cannot add constraints directly between the Label and the Navigation Bar, I add my constraint between the Top of the Label and the Top of the it's Parent View. I set the constant of the contstraint to be equal to the height of the Status Bar + the height of the Navigation Bar.

The issue I have is during rotation between Portrait and Landscape, because the height of the Navigation Bar changes and I need my Label to rotate nicely as well, so I need to know the new height of the Navigation Bar in the willRotateToInterfaceOrientation: method.

I use this method to ensure the Label is in the correct location when the View Controller is navigated to from either portrait or landscape. It works fine.

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    // Get Y origin for Label
    CGFloat navBarY = self.navigationController.navigationBar.frame.origin.y;
    CGFloat navBarHeight = self.navigationController.navigationBar.frame.size.height;
    CGFloat labelY = navBarY + navBarHeight;

    // Set vertical space constraint for Label
    self.labelConstraint.constant = labelY;
}

I use this method to reposition the Label when the orientation is changed, as the Navigation Bar height changes from 44px to 32px. Problem is, I need to get the NEW height that the navigation bar WILL BE after the rotation, BEFORE the rotation actually takes place.

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];

    // Get Y origin for Label
    CGFloat navBarY = self.navigationController.navigationBar.frame.origin.y;
    CGFloat navBarHeight = self.navigationController.navigationBar.frame.size.height;
    CGFloat labelY = navBarY + navBarHeight;


    // This obviously returns the the current Y origin for label before the rotation
    // Which isn't very useful.

    NSLog(@"labelY: %f", labelY);


    // This code produces the desired effect, but obviously I want to avoid
    // hard-coding the values.

    if (UIInterfaceOrientationIsLandscape(toInterfaceOrientation)) {
        self.labelConstraint.constant = 20 + 32;
    } else {
        self.labelConstraint.constant = 20 + 44;
    }
} 

For fun, I tried to set the Y origin for the Label after the rotation in didRotateFromInterfaceOrientation:, but as expected it's not smooth and the label snaps into place after the rotation is complete.

Thanks in advance for your assistance!

4

1 回答 1

0

The answer is: Size Classes. You can inform yourself about it, it's well documented by Apple. You will have to use this function: func willTransitionToTraitCollection(newCollection: UITraitCollection, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator). When setting up your view I would suggest adding two properties in your view controller, both an array of NSLayoutConstraint, one for portrait mode, one for landscape. Since iOS 8, there is the possibility to activate/deactivate multiple constraints at once. In the above mentioned function, do the following:

if newCollection.verticalSizeClass == .Compact { //orientiation is landscape
    NSLayoutConstraint.deactivateConstraints(self.portraitConstraints)
    NSLayoutConstraint.activateConstraints(self.landscapeConstraints)
} else {
    NSLayoutConstraint.deactivateConstraints(self.landscapeConstraints)
    NSLayoutConstraint.activateConstraints(self.portraitConstraints)
}

Make sure to deactivate first, otherwise your constraints will conflict.

Sorry for the "swifty" answer, I suppose you'll be able to 'translate' it into ObjC. If you have any question, feel free to ask.

于 2015-04-11T21:47:30.557 回答