3

I'm trying to make the UINavigationController's UINavigationItem retract upon scrolling a WebView's UIScrollView (just like it does in Safari).

Desired behaviour

This code works on iOS 12:

let statusBar: UIView = UIApplication.shared.value(forKey: "statusBar") as! UIView
if statusBar.responds(to:#selector(setter: UIView.backgroundColor)) {
    statusBar.backgroundColor = UIColor.LinguaBrowse
}

See how the WKWebView's content extends only as far as the UIStatusBar when the UINavigationBar retracts, and note how the UIStatusBar is coloured and opaque.

iOS 12 visible iOS 12 retracted

Images: (1) Navbar visible; (2) Navbar retracted.

Actual behaviour since iOS 13

While I can colour the statusbar in iOS 12, I can't do so in iOS 13 as they've changed the APIs of status bar (you can no longer get a reference to it), so the original methodology no longer works.

This code snippet has been suggested in Swift: Change status bar color for iOS 13 and Change status bar colour on iOS13, but it acts very differently (quite apart from the fact that it doesn't respond to changes in device orientation).

if #available(iOS 13.0, *) {
    if let firstWindow = UIApplication.shared.windows.first,
        let statusBarFrame = firstWindow.windowScene?.statusBarManager?.statusBarFrame {
        let mockStatusBar = UIView.init(frame: statusBarFrame)
        mockStatusBar.backgroundColor = UIColor.red
        firstWindow.insertSubview(mockStatusBar, at: 1)
    }
}

Note: I've coloured the mock status bar red for ease of debugging, but I'd ultimately change it to a matching blue colour once finding a working setup.

  • In screenshot (4), I show what the status bar looks like without having applied that code (it looks fine, as the toolbar's background colour fills it).

  • In screenshot (5), I show how the status bar looks if we use that code snippet to make a red mock status bar.

  • In screenshot (6), I show how, whether we've executed that code snippet or not (it's the same either way), the status bar is found to be transparent and the scrollview extends all the way to the top of the UIViewController. The view debugger shows that the red mock status bar is in the right position, but is somehow (despite being a child of the UIWindow) at a layer below that of the UIScrollView. So I don't think that adjusting its z-index will change anything.

iOS 13 visible - default statusbar iOS 13 visible - red mock statusbar iOS 13 retracted

Images: (4) Navbar visible (statusbar left unchanged); (5) Navbar visible (with mock statusbar inserted); (6) Navbar retracted - same result for either approach.


I've been trying various different approaches for days and can't find any way to restore the behaviour that I had working in iOS 12. I just want an opaque, coloured status bar (or something that looks and acts like it). Does anyone have some guidance?

Jamie Birch
  • 5,839
  • 1
  • 46
  • 60
  • Why are you adding the view to the window and not to the viewController's view? I think that when the view controller is added to the window is added above the mockStatusBar. You can try to save the mock status bar in a var and bring it to front when desired. – LorenzOliveto Nov 14 '19 at 10:36
  • @LorenzOliveto I did try making a container view that holds the mockStatusBar on top and the scrollView below, then tried setting that container view as the view controller's view, but I couldn't get all the constraints to work correctly. The navigation bar and its retraction animation add significant complexity to the whole setup. But it may have to be the way forward. I'll keep waiting for further ideas for now. – Jamie Birch Nov 14 '19 at 11:09

0 Answers0