0

There are many answers in SO that provide solutions for hiding the navigation bar shadow. Those work for me except for this particular case, which I'm describing here. Therefore, this question is not a duplicate.

To test this particular case, I created a new project using the master-detail app template. In the DetailViewController -> viewDidAppear, I coded the following:

    self.navigationController?.navigationBar.setBackgroundImage(UIImage(), for: UIBarMetrics.default)
    self.navigationController?.navigationBar.shadowImage = UIImage()

The above code works for a Single View App and on iPad Air 2 simulator. However, it doesn't work on the detailViewController of a master-detail app in iPhoneX simulator.

Alternatively, I also tried retrieving the subviews of the navigationBar in viewDidAppear and tried hiding the shadow (see code below). However, the subview count is zero. How could that be?

    for parent in self.navigationController!.navigationBar.subviews {
        for childView in parent.subviews {
            if(childView is UIImageView) {
                childView.removeFromSuperview()
            }
        }
    }

Any help on this is much appreciated.

Image of my storyboard

Community
  • 1
  • 1
Felix Marianayagam
  • 2,634
  • 2
  • 9
  • 29

1 Answers1

2

For Generic flow

You could use this setup. Say ViewController is the all other view controller class (where you want the shadow), and DetailViewController is the detail view controller class.

The thing i'm doing preserving the shadow image.

ViewController.swift

class ViewController: UIViewController {

    var shadowImage: UIImage!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

         shadowImage = self.navigationController?.navigationBar.shadowImage
    }


    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        self.navigationController?.navigationBar.shadowImage = shadowImage
    }

}

And DetailViewController.swift

class DetailViewController: UIViewController {


    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        self.navigationController?.navigationBar.shadowImage = UIImage()
    }
}

Storyboard setup enter image description here

Output

enter image description here

Note: Another neat approach would be storing the shadow within the DetailsViewController and setting it while the view is about to disappear

class DetailsViewController: UIViewController {

    var shadowImage: UIImage!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        shadowImage = self.navigationController?.navigationBar.shadowImage
        self.navigationController?.navigationBar.shadowImage = UIImage()
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        self.navigationController?.navigationBar.shadowImage = shadowImage
    }
}

This solution is more elegant and resulting in a clean management.

For MasterDetailFlow, Using SplitViewController

In your MasterViewControlelr.swift

override func viewWillAppear(_ animated: Bool) {
    clearsSelectionOnViewWillAppear = splitViewController!.isCollapsed // placeholder code when you created the project
    super.viewWillAppear(animated)
    self.navigationController?.navigationBar.shadowImage = nil
}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    self.navigationController?.navigationBar.shadowImage = UIImage()
}

In your DetailViewController.swift

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    self.navigationController?.navigationBar.shadowImage = UIImage()
}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    self.navigationController?.navigationBar.shadowImage = nil
}

Output(Master/Detail flow)

enter image description here

Ratul Sharker
  • 7,484
  • 4
  • 35
  • 44
  • I need the splitViewController. In your solution, you have removed it. Why doesn't it work with the splitViewController? – Felix Marianayagam Nov 18 '18 at 16:23
  • 1
    Great! You nailed it. Thanks. – Felix Marianayagam Nov 18 '18 at 18:39
  • In the DetailsViewController (iPhone), if I add a toggle button to hide or show shadow, it doesn't work. The same works in iPad. Can you please help? – Felix Marianayagam Nov 19 '18 at 12:39
  • Could you show me the code, how you are handling the toggling ? – Ratul Sharker Nov 19 '18 at 12:43
  • In DetailsViewController, I added a switch control and the following code: @IBAction func toggleShadow(_ sender: UISwitch) { if sender.isOn { self.navigationController?.navigationBar.shadowImage = nil } else { self.navigationController?.navigationBar.shadowImage = UIImage() } } Note: This code works in iPad but not in IPhone. – Felix Marianayagam Nov 19 '18 at 13:18
  • Are you still with the splitViewController ? – Ratul Sharker Nov 19 '18 at 13:20
  • Yes. I'm using splitViewController. – Felix Marianayagam Nov 19 '18 at 13:24
  • 1
    Then only setting the `DetailsViewController`'s navigationController won't work, you have to toggle the shadow image of the `MasterViewController` too. For fast pacing testing use `NSNotification`, for more efficient one, use protocol to fireback the shadow image change event to the `MasterViewController`. – Ratul Sharker Nov 19 '18 at 13:27
  • That works on iPhone. For iPad, when the master and detail views are side by side, it removes the shadow for both, where as I need the shadow toggled only for the detail. Do I need to check if it's iPhone or iPad and perform the task? or is there any other efficient method. – Felix Marianayagam Nov 19 '18 at 14:05
  • Check it out. https://stackoverflow.com/questions/25436387/detect-when-both-the-master-and-detail-view-controller-are-on-screen . Look for the `collapse` property, check against it. – Ratul Sharker Nov 19 '18 at 14:13