4

I have a Storyboard-based iOS Swift app that has a UITabBarController as the main view controller which in turn has four tabs. When initializing, I want to pre-load all of them in order to have them register in my global data, like this:

Main view controller:

class MainViewController : UITabBarController {

    override func viewDidLoad() {
        super.viewDidLoad()
        for viewController in self.viewControllers!
        {
            let v = viewController.view;
            print("ViewController: \(v)")
        }
    }

Accessing the .view should trigger viewDidLoad() but this does not work here apparently.

Sub view controller:

class Sub1ViewController: UITableViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        Global.sub1vc = self    // register in global data
    }
}

The reason is that the sub controllers' tabs should show badges with information even if the tab was not yet selected by the user.

When the program is started, all four sub VCs are listed in the print output, but only for no. 2 the viewDidLoad() method is called (although it is actually not a direct child of the UITabBarController but there is a SplitViewController in between). After that, sub VC no. 1's viedDidLoad() is called as it is actually shown in the display. Only when no. 3 or 4 are actually selected by the user, their viewDidLoad() is called.

How can I accomplish this the "right" way? Do I have to implement init methods for all sub controllers?

Addition:

I seem to have kind of a fix for this now: In the main view controller's viewDidLoad() I now do this:

override func viewDidLoad() {
    super.viewDidLoad()
    for viewController in self.viewControllers!
    {
        if viewController.title == "No. 2" {
            if let navctr = viewController as? UINavigationController,
                   ctr = navctr.viewControllers[0] as? Sub2ViewController {
                        G.sub2vc = ctr
            }
        }
    }
}

This works, but it relies on having (1st) a unique title that is given to the topmost sub controller (a UINavigationController in this case, but other types can apply as well for other tabs) and (2nd) actually knowing, i.e. coding, what way to use to access the actual sub controller from there (walking through the navigation controller in this case). I must admit having the respective target controller registering itself seems more elegant and less error-prone to me as there are no extra dependencies.

Stefan
  • 1,036
  • 10
  • 32

3 Answers3

1

The clean way to do it is to update the badge information in awakeFromNib of each view controller instead of viewDidLoad.

Cœur
  • 37,241
  • 25
  • 195
  • 267
0

It sounds like that whatever logic that depends on Global.sub1vc should be reworked. Fudging UIKit to load all of your ViewControllers is not the answer.

chedabob
  • 5,835
  • 2
  • 24
  • 44
  • OK, I see. The current mechanism is to call (e.g.) `Global.sub2vc.doSomething()` which in turn, after some processing, sets its `badgeValue`. This requires differentiating between the sub controllers as each has a different task. What would a better architecture look like? More extensive separation between UI and data mechanics? – Stefan Jan 11 '16 at 10:51
-2

In your MainViewController in viewDidLoad, you could set the selectedIndex to load each view:

// Load all views
for num in 0...self.tabBar.items!.count {
    self.selectedIndex = num
}

// Set default index
self.selectedIndex = 0;
Richard Erickson
  • 2,568
  • 8
  • 26
  • 39