4
votes

In my iOS 9+ app I use UINavigationBar in some of the UIViewControllers "manually" (= placing them directly in the VC instead of using UINavigationViewController).

Setting a height for such a UINavigationBar using a simple contraint was no problem before. However in iOS 11 the bar itself still correctly uses the assigned size, but the content is not positioned correctly any more (is now alligned to the top).

enter image description here

Any idea how to solve this?

EDIT:

I am pretty sure, that this is not a duplicate of "customizing iOS 11 navigation bar height customizing", this this question addresses a problem with UINavigationBar subclasses while I directly use UINavigationBar. Additionally the described problem seems to solved since Beta 4, while I am experiencing the problem in Beta 6.

EDIT 2:

Meanwhile I implemented a UINavigationBar subclass as proposed by CharlieSu in the other thread. However this did not solve the problem in my case. It seems that the subview frame are all set properly (without doing this manually), but the layout is broken anyway. So setting the frames manually does not make any difference.

3
Dont change the bar height, just use custom view on top of it or hide the bar, ios lower than 11 seems to have that issueTj3n
This would be a solution, however it would be pretty cumbersome if the UINavigationBar uses some kind of gradient background. Do you see the issue on the side of iOS 10 or below? I would say, that iOS 10- work as expected and that the iOS 11 layout is broken.Andrei Herford

3 Answers

2
votes

I'm facing this problem right now. At first, It appears to be another height constraint in the bar that conflicts with mine, but mine has 1000 priority so it seems not to be the problem. Then I see another view inside UINavigationBar "UINavigationBarContentView", this view has height bigger than 0.

I then tried to set navigationBar.clipsToBounds = true and it works, cause parent view has the correct height...

EDIT: If you need to show the shadow of the bar, you will need clipToBounds = false. In that case you can subclass the NavigationBar

import UIKit

class SecondNavigationBar: UINavigationBar {
    override func layoutSubviews() {
        super.layoutSubviews()

        for subview in self.subviews {
            var stringFromClass = NSStringFromClass(subview.classForCoder)
            print("--------- \(stringFromClass)")
            if stringFromClass.contains("BarBackground") {
                subview.frame = self.bounds
            } else if stringFromClass.contains("UINavigationBarContentView") {
                subview.frame = self.bounds
            }
        }
    }
}
2
votes

I've had this problem, too. I solved it this way:

 -(void)layoutSubviews{
        [super layoutSubviews];
        CGRect rectStatus = [[UIApplication sharedApplication] statusBarFrame];
        if (rectStatus.size.height==44.f) {

        }else{
            if (@available(iOS 11.0, *)) {
                for ( UIView*aView in self.subviews) {
                    if ([NSStringFromClass(aView.classForCoder) isEqualToString:@"_UINavigationBarContentView"]) {
                        aView.frame = CGRectMake( 0,20,aView.frame.size.width,44);
                    }
                    else if ([NSStringFromClass(aView.classForCoder) isEqualToString:@"_UIBarBackground"]) {
                        aView.frame = CGRectMake(0,0,aView.frame.size.width, 64);
                    }
                }
            }
        }
    }
0
votes

In iOS 11, we can directly override barPosition in subclass of UINavigationBar (return UIBarPositionTopAttached instead default value UIBarPositionTop ) when u use custom UINavigationBar in UIViewController
like this :

- (UIBarPosition)barPosition {
    return UIBarPositionTopAttached;
}

the layout code will be like this:

    NSLayoutConstraint* a = [navigationBar.widthAnchor constraintEqualToAnchor:viewController.view.widthAnchor];
    NSLayoutConstraint *b = [navigationBar.centerXAnchor constraintEqualToAnchor:viewController.view.centerXAnchor constant:0];
    NSLayoutConstraint* c = [navigationBar.centerYAnchor constraintEqualToAnchor:viewController.view.topAnchor constant:X];

   [NSLayoutConstraint activateConstraints:@[a,b,c]];   

Don't set it's height ,it should always be 44.

It's works well in iOS 11. The view hierarchy is as same as UINavigationController's.
But it doesn't work below iOS 11. We need set the UINavigationBarDelegate to return UIBarPositionTopAttached in

- (UIBarPosition)positionForBar:(id <UIBarPositioning>)bar;