12

just downloaded xcode 9 and i'm having this weird issue, on ios 11 my custom navbar appears to be half in size and is under the status bar, on ios 10 works fine.

so here is my code

let newNavbar: UINavigationBar = UINavigationBar(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: 64))
let navItem = UINavigationItem()

//create and assign the items

newNavbar.setItems([navItem], animated: false)
view.addSubview(newNavbar)

here is a screenshot, ios11 on the left and ios10 on the right,

enter image description here

rmaddy
  • 314,917
  • 42
  • 532
  • 579
iknowNothing
  • 389
  • 1
  • 2
  • 13
  • This looks related: https://stackoverflow.com/questions/46275652/how-to-prevent-app-from-overlapping-status-bar-in-ios-11-iphone-x-xcode-9/ – finneycanhelp Oct 08 '17 at 18:27

5 Answers5

28

Your code was always wrong. You should not be setting the height of a manually added navigation bar yourself or placing it at the top of the view. You should pin the top of the navigation bar to the bottom of the status bar (e.g. the top of the Safe Area) and give it a delegate so that you can use the UIBarPositioningDelegate mechanism to set its position to .topAttached, which will cause it to stretch up to the top of the screen correctly.

(But you should also be asking yourself why you are adding a navigation bar manually. There is usually no reason not to wrap your view controller in a UINavigationController — even if you don't intend to do any actual navigation — just to get the navigation bar, with all its automatic management.)

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • i have to create a custom navbar though, can i create a new one without providing height and adding it to the view? the reason is in my nested Detail View i'm using a UIimage as a navbar with a blur, using the default one ruins that. – iknowNothing Sep 20 '17 at 16:52
  • 1
    Try doing what I suggested, then. – matt Sep 20 '17 at 16:54
  • about the first suggestion it doesn't seem i can use it with a custom navbar. about the second i tried the system navbar but it causes another issues with the nested Detail view, see here https://i.imgur.com/PAb5Thy.png left with custom navbar and right with the default. creating my sutom navbars worked well until ios11 – iknowNothing Sep 20 '17 at 17:19
  • "about the first suggestion it doesn't seem i can use it with a custom navbar" Then you're not doing what I said, because I do this with success. – matt Sep 20 '17 at 17:22
  • after messing with it for some time i ran into the same issue see here https://i.imgur.com/NUYcdp4.png i've added a delegate and confirmed .topAttached function is executing. what am i doing wrong? – iknowNothing Sep 20 '17 at 21:22
  • 2
    You have a top space constraint of zero to the superview. That is not what I said to do. In fact, it is exactly what I said _not_ to do. – matt Sep 20 '17 at 22:06
  • 1
    dammit, you're right its working now, thanks i really appreciate it. – iknowNothing Sep 21 '17 at 08:44
  • You are saying here that there is no reason to add a navigation bar manually. What about when you are using a modal segue? Even if your VC is embedded into a navigation controller, the nav bar does not show up when using a modal segue. Am I doing something wrong here? – Nevin Jethmalani Sep 25 '17 at 14:55
  • 1
    @NevinJethmalani Yes, you must be. Nothing prevents modal segue to navigation controller. If you want help, ask this as a real question so I can see the actual problem. Thanks! – matt Sep 25 '17 at 15:39
  • @matt This is saying that the nav bar disappears. If this is wrong as well, I will go ahead and open up a new question. https://stackoverflow.com/questions/15689261/modal-segue-navigation-bar-disappears – Nevin Jethmalani Sep 25 '17 at 19:34
  • @NevinJethmalani The nav bar doesn't "disappear". If you do a modal segue, you are covering the whole screen, including the nav bar. No surprise there. – matt Sep 25 '17 at 19:41
  • @matt Perfect. Sorry for the confusion. That is what I thought as well. Is it better practice to embed the view in a new navigation controller or just put a nav bar in place when we segue modally? This is to display a shopping cart from the main view for example. – Nevin Jethmalani Sep 25 '17 at 20:15
  • As I said in my answer, if you need a nav bar in the presented view controller, then segue to a navigation controller. But remember, you'll still need a way to dismiss; you can't go "Back" to dismiss. – matt Sep 25 '17 at 20:19
  • @matt Can you comment on how to actually "pin the top of the navigation bar to the bottom of the status bar"? As far as I'm aware, there is no reference available to the status bar view (just the frame). I'm assuming you meant setting an autolayout constraint that does this. – Apoorv Khatreja Oct 23 '17 at 20:35
  • 1
    @ApoorvKhatreja In iOS 11 / Xcode 9 it would be the safe area layout guide top anchor. Before that it would be the bottom of the top layout guide. – matt Oct 23 '17 at 23:19
  • @matt you are the saviour. I have been pulling my hair out for past 4 hours as to why my `setBackgroundImage(_, for:, barMetrics:)` was not working even when I set `UIBarPosition` to `topAttached`. – nefarianblack Jun 05 '18 at 10:09
6

See ios 11 custom navbar goes under status bar / ios 11 navigation bar overlap status bar for an answer

Not sure if this is the same issue, but we ran into this as well when upgrading to iOS 11.

See ios 11 custom navbar goes under status bar

We were manually setting nav bar height to 64 and pinning to the superview edges. Conforming to the UINavigationBarDelegate protocol and implementing the UIBarPositioningDelegate delegate method solved it for us.

We replaced

navigationBar.autoPinEdgesToSuperviewEdgesExcludingEdge(.bottom)
navigationBar.autoSetDimension(.height, toSize: 64)

with

...
  if #available(iOS 11.0, *) {
    navigationBar.topAnchor.constraint(
      equalTo: self.view.safeAreaLayoutGuide.topAnchor
    ).isActive = true
  } else {
    navigationBar.topAnchor.constraint(
      equalTo: topLayoutGuide.bottomAnchor
    ).isActive = true
  }
  navigationBar.autoPinEdge(toSuperviewEdge: .left)
  navigationBar.autoPinEdge(toSuperviewEdge: .right)
  navigationBar.delegate = self
...

public func position(for bar: UIBarPositioning) -> UIBarPosition
  return .topAttached
}

This is using the purelayout DSL for some of the autolayout calls (https://github.com/PureLayout/PureLayout)

Credit goes to https://stackoverflow.com/users/341994/matt for an answer

Darren Cheng
  • 1,435
  • 14
  • 12
3

Try adding some Auto Layout constraints after you add the nav bar to the view

if #available(iOS 11.0, *) {
    newNavbar.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
    newNavbar.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
    newNavbar.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
    newNavbar.heightAnchor.constraint(equalToConstant: 64).isActive = true
}

You can actually use all but the third constraint in earlier versions of iOS but if it all works in earlier versions you may not want to mess with it.

Using the safe layout area should keep your nav bar under the status bar.

theMikeSwan
  • 4,739
  • 2
  • 31
  • 44
  • we are getting crash when try to set a new constraints which you have mentioned – dineshprasanna Jun 24 '18 at 08:29
  • @dineshprasanna - have you solved this crash issue? what is the problem? I too get a crash saying "they have no common ancestor. Does the constraint or its anchors reference items in different view hierarchies? That's illegal." – Lohith Korupolu Feb 25 '20 at 10:53
1

First of all, make sure that your navigation controller's "shows navigation bar" is unchecked in storyboard.

Then, drag and drop navigation bar from the "object library". Give Top constraint equal to 20.

It'll also work perfect in "iPhone X simulator".

Happy Coding!

MyApp
  • 11
  • 1
1

Tried this solution in Swift 4.1

let menuBar: UIView = {
let mb = UIView()
mb.backgroundColor = .red
mb.translatesAutoresizingMaskIntoConstraints = false
return mb
}()



private func setupMenuBar(){

view.addSubview(menuBar)

let constraints = [ menuBar.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
                    menuBar.leadingAnchor.constraint(equalTo: view.leadingAnchor),
                    menuBar.trailingAnchor.constraint(equalTo: view.trailingAnchor),
                    menuBar.heightAnchor.constraint(equalToConstant: 50)]
NSLayoutConstraint.activate(constraints)
}
MJ Montes
  • 3,234
  • 1
  • 19
  • 21