-1

I have a UIViewController that implements MGLMapViewDelegate. I'm trying to place a nav bar at the top, with two buttons.

I couldn't get my buttons or nav bar visible so I tried using the function view.bringSubviewToFront() within my viewDidLoadFunction() as suggested on this post

When I did this, I received the error

Error: Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value

I don't understand how the IBOutlet variables can ever be nil, I thought they were assigned to the storyboard buttons


My ViewController Class(At least the parts that I think are important)

class ViewController: UIViewController, MGLMapViewDelegate {

...

var mapView: MGLMapView?
@IBOutlet var navBar: UINavigationBar!
@IBOutlet var signOutButton: UIButton!
@IBOutlet var newPostButton: UIButton!

...

override func viewDidLoad() {
            super.viewDidLoad()

    self.mapView = MGLMapView(frame: view.bounds, styleURL: MGLStyle.lightStyleURL)

    ...

        view.addSubview(self.mapView!)

        view.addSubview(navBar)
        view.addSubview(signOutButton)
        view.addSubview(newPostButton)
        //Make the navBar and Buttons visible
        view.bringSubviewToFront(self.navBar)
        view.bringSubviewToFront(self.signOutButton)
        view.bringSubviewToFront(self.newPostButton)

        ...
}

...

@objc func handleLogout() {
     let loginController = SignInViewController()

     do {
          try Auth.auth().signOut()
      } catch let logoutError {
          print(logoutError)
      }

      present(loginController, animated: true, completion: nil)
  }
}

Some screenshots of my storyboard I thought might be important

enter image description here enter image description here enter image description here enter image description here enter image description here

I've tried placing this in my view controller right after super.viewDidLoad()

self.storyboard?.instantiateViewController(withIdentifier: "mainViewController") as! ViewController

I've tried bringing the view to the front of the mapview instead of the main view

self.mapView.bringViewToFront(navBar)

I've tried placing the code inside functions like awakeFromNib and viewDidLayoutSubviews

    override func awakeFromNib() {
        super.awakeFromNib()

        //Make the navBar and Buttons visible
        self.mapView!.bringSubviewToFront(self.navBar)
        self.mapView!.bringSubviewToFront(self.signOutButton)
        self.mapView!.bringSubviewToFront(self.newPostButton)

    }
Revanth Kausikan
  • 673
  • 1
  • 9
  • 21
Sam
  • 1,765
  • 11
  • 82
  • 176
  • Did you check if your mapView is nil? – Teetz Apr 05 '19 at 13:52
  • why view.addSubview(signOutButton) if they already are in the storyboard? – Alastar Apr 05 '19 at 13:53
  • @Teetz I didn't, but it happens for just `view.addSubview` which wouldn't be mapView. I'll try though – Sam Apr 05 '19 at 13:54
  • @Alastar That's a debugging attempt at making sure the buttons are in the view, it wasn't present on my first attempt. I could take it out if you think that would make it more clear or better? – Sam Apr 05 '19 at 13:55
  • `var mapView: MGLMapView?` has to be the MapView in the ViewController Scene? – Alastar Apr 05 '19 at 14:06
  • @Alastar I'm using MapBox, it's very similar to mapkit but there's a few other things that belong to it. I can usually use instructions that are meant for mapkit, and they work with mapbox except for changing the class names from something like MKMapView to MGLMapView. – Sam Apr 05 '19 at 14:19
  • @Jacob - put a breakpoint at `super.viewDidLoad()` and step through... Make sure you know exactly which line is throwing the "Unexpectedly found nil" error. – DonMag Apr 05 '19 at 14:25
  • check whether mapview is nil after allocation. – Abhijith Apr 05 '19 at 14:27
  • @DonMag It shows the "Thread 1" error right beside view.addSubview(navBar), if I remove navBar it shows it beside `view.addSubview(signOutButton)`, I haven't gone into these functions because I know it will be a bunch of stuff I don't understand and probably won't help me much. I could try though if there's something specific you think I should look for inside them? – Sam Apr 05 '19 at 14:27
  • @AbhijithPurushothaman Both view and mapview are non-nil optionals. The other 3 vars print nil – Sam Apr 05 '19 at 14:30
  • 1
    @Jacob - do you have more than one Storyboard in your project? If so, is it possible you have your Main Interface set to a different Storyboard, with a different View Controller? – DonMag Apr 05 '19 at 14:34
  • @DonMag I have launchScreen.storyboard, which I think is there when I create the project. I haven't used it or touch it really. I do have 2 scenes in my storyboard though. I have a login page, which is the first page, and the next page(the mapview) is opened up by calling `let mainController = ViewController()` and then `self.present(mainController, animated:true, completion:nil)` – Sam Apr 05 '19 at 14:40
  • Are you accessing these IBOutlets values after instantiation but before presenting/Pushing ? Can you add the code how you present/alocate this view controller ? – Abhijith Apr 05 '19 at 14:42
  • This is wrong --> let mainController = ViewController() Use instantiateViewController, then push/present – Abhijith Apr 05 '19 at 14:44
  • http://www.programmingios.net/dont-make-a-new-instance-by-mistake/ – matt Apr 07 '19 at 19:04

1 Answers1

3

From your comments, I understood that you are instantiating the controller wrongly.

let mainController = ViewController(). // Wrong

This will just instantiate the ViewController but not any objects from the story board.

Use below code to instantiate viewcontroller from storyboard.

    if let vc = self.storyboard?.instantiateViewController(withIdentifier: "mainViewController") as? ViewController {
        //Set any data if required, then present
        self.present(vc, animated: true, completion: nil)
    }

If you use multiple storyBoards,

 let storyboard = UIStoryboard(name: "Main", bundle: nil)
 if let vc = storyboard.instantiateViewController(withIdentifier: "mainViewController") as? ViewController {
        self.present(vc, animated: true, completion: nil)
   }
Abhijith
  • 3,094
  • 1
  • 33
  • 36
  • I tried this and nothing happened, so I tried `dump(self.storyboard)` and got nil. I feel like it's worth mentioning that the class that called self.storyboard isn't from the storyboard. It's a view controller I wrote in code. Is the easiest way to create a view controller pane in my storyboard for this class? – Sam Apr 05 '19 at 15:09
  • My login storyboard scene goes to the mainView, but it also goes to some other view controllers that link to the main view, so sorry if that confused you – Sam Apr 05 '19 at 15:13
  • So I used `if let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "mainViewController") as? ViewController {` and it worked. Your answer is the way to do it though if your following good practice though. Maybe add a note about how if the view is on a different storyboard you can call `UIStoryboard(name: "Main", bundle: nil).instantiateViewController`? It's up to you, i'm sure someone will be glad you did though. – Sam Apr 05 '19 at 15:20
  • Yeah I did that..! – Abhijith Apr 05 '19 at 15:23