Search code examples
swiftmodal-dialogstoryboarduinavigationbarios13

Navigation Bar's content partially not visible in modal on iOS 13


A storyboard based application is having issue rendering the navigation bar's content when the navigation bar is displayed in a modal screen, but only when on a physical device. The code behaves properly in iOS 12 and in all simulators both iOS 12 and iOS 13.2.2.

comparative screenshot simulator vs physical device

On the left of the screenshot is a iPhone 11 simulator running iOS 13.2.2; on the right is a Reflector projection of my iPhone Xs running iOS 13.2.2 of the same code. We can see there's a space between the tableview and the navigation bar content on the physical device, but on the simulator the tableview is flush against the navigation bar.

There are no table section view headers, tableview margins are set to safe area. Has anyone else experienced that issue and if so, how did you solve it?

Here's a snapshot of the stackview captured from the device, in which we can clearly see the area being mis-rendered is well within the margins of the UINavigationBar: Xcode views stack capture

I was also able to replicate the issue on a brand new project when setting up the following view structure:

Storyboard layout of view structures to replicate the issue

The code to run this demo project is available on GitHub at: https://github.com/ekscrypto/stackoverflow-59033294

With the differing behaviours: Demo of minimum reproducible build

We can see physical device showing a bar of red between the navigation bar's content and the tableview; but that red bar is not visible in the simulator.


edit: 2019-11-25 16:45 EDT -- As per comments below I tried to force a refresh of the layout using:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    if #available(iOS 13.0, *) {
        DispatchQueue.main.async {
            self.navigationController?.navigationBar.setNeedsLayout()
            self.navigationController?.navigationBar.layoutIfNeeded()
        }
    }
}

With and without the dispatch async, as well as with and without the layoutIfNeeded; it did not solve this particular issue for me.


Solution

  • Based on the answer at How to prevent gap between uinavigationbar and view in iOS 13?, which wasn't working for me, I solved my issue using the following code:

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        if #available(iOS 13.0, *) {
                self.navigationController?.setNavigationBarHidden(true, animated: false)
                self.navigationController?.setNavigationBarHidden(false, animated: false)
        }
    }
    

    Or in Objective-C:

    -(void)viewWillAppear:(BOOL)animated {
        [super viewWillAppear:animated];
        if(@available(iOS 13, *)) {
            [self.navigationController setNavigationBarHidden:true animated:false];
            [self.navigationController setNavigationBarHidden:false animated:false];
        }
    }