Search code examples
iosswiftdelegatesuitabbarcontrolleriboutlet

Programmatically switching tabBar causes IBOutlet to be nil


I am switching tabBar programmatically - which works fine. However, an IBOutlet mapView (google maps) becomes nil - leading to a crash...

I've spent quite a few hours on this. Feels like I am missing something trivial. Looked through SO, e.g.: Switch tab bar programatically in Swift and All my IBOutlet are nil in viewDidLoad but with no luck.

Any help much appreciated!

App delegate

func goToTabRoot(){
    if let tabBarController = self.window!.rootViewController as? UITabBarController {
        let index = 0 // First (root) tab
        tabBarController.selectedIndex = index
        let vc = tabBarController.viewControllers![index] as! UINavigationController
        tabBarController.delegate?.tabBarController!(tabBarController, didSelectViewController:vc)
    }
}

Map View Controller

class MapViewController: UIViewController, CLLocationManagerDelegate, GMSMapViewDelegate, BudgetTableViewControllerDelegate, StoreViewControllerDelegate, ProductPickedViewControllerDelegate {

@IBOutlet weak var mapView: GMSMapView!

override func viewDidAppear(animated: Bool) {
    locationManager.delegate = self
    locationManager.desiredAccuracy = kCLLocationAccuracyBest // Best accuracy
    locationManager.requestWhenInUseAuthorization()
    mapView.delegate = self // <== ERROR HERE. mapView = nil

    println("ViewDidAppear called")
}


func favoriteViewDidFinish(controller: MapViewController, productIds: [Product]) {
    println("Favorite finished")

    // Select MapView
    let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
    appDelegate.goToTabRoot()

    // Store product IDs
    self.productIds = productIds

    // Update stores (download)
    queryType = "filterProducts"
    downloadStores = true
    updateStores()

}

Products selected

protocol ProductPickedViewControllerDelegate: class {
    func favoriteViewDidFinish(controller: MapViewController, productIds: [Product])
}

class ProductPickedViewController: UIViewController, UITabBarControllerDelegate {

var delegate:ProductPickedViewControllerDelegate? = nil

@IBAction func storesButtonPressed(sender: AnyObject) {

    // Set delegate
    delegate = MapViewController() // First view controller

    // Download relevant stores
    println("Should reload stores...")
    if (self.delegate != nil) {
        println("activate delegate")
        self.delegate!.favoriteViewDidFinish(MapViewController(), productIds: productsPresented)
    }

Solution

  • The problem is that you're creating a new instance of MapViewController when you set the delegate with the below line. That instance isn't made in the storyboard, so it knows nothing about your IBOutlet.

    delegate = MapViewController()
    

    You need to get a reference to the controller that already exists. If ProductPickedViewController is one of the view controllers in the tab bar controller, you can do that like so,

    delegate = self.tabBarController!.viewControllers[0] as! MapViewController