Search code examples
swiftuicontainerviewchildviewcontrollerrootviewcontrollerkeywindow

loading childViewControllers first time logging in


I have been struggling with an issue for loading child ViewControllers. The first time I log in it shows the containerView but without the childViewControllers loaded. When I close the app and re-open the app with the logged in state saved the childViews in the containerView are displayed. I know it has something to do with my window hierarchy and rootviewController. I have researched this a bunch but still am having issues solving. Thanks in advance!

// app delegate @UIApplicationMain

class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

        window = UIWindow(frame: UIScreen.main.bounds)
        window?.makeKeyAndVisible()
        window?.rootViewController = MainNavigationController()

        return true
    }

// mainNavigationController - RootViewController

class MainNavigationController: UINavigationController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let vc1 = TravelersFeedVC()
        let vc2 = ProfileVC()

        if isLoggedIn() {
            // assume user is logged in
            let homeController = HomeController()
            homeController.firstViewController = vc1
            homeController.secondViewController = vc2

            viewControllers = [homeController]

        } else {
            perform(#selector(showLoginController), with: nil, afterDelay: 0.01)
        }

    }

    fileprivate func isLoggedIn() ->  Bool {
        return UserDefaults.standard.isLoggedIn()
    }

    func showLoginController() {
        let loginController = LoginController()
        present(loginController, animated: true, completion: {
            // perhaps do something here later
        }) 
    }
}

// log in function

    func finishLoggingIn() {
        let rootViewController = UIApplication.shared.keyWindow?.rootViewController
        guard let mainNavigationController = rootViewController as? MainNavigationController else { return }
        mainNavigationController.viewControllers = [HomeController()]

        let vc1 = TravelersFeedVC()
        let vc2 = ProfileVC()

        let homeController = HomeController()
        homeController.firstViewController = vc1
        homeController.secondViewController = vc2

        UserDefaults.standard.setIsLoggedIn(value: true)
        dismiss(animated: true, completion: nil)
    }

// HomeController

class HomeController: UIViewController, FBSDKLoginButtonDelegate {

    // child view controllers to put inside content view
    var firstViewController: TravelersFeedVC?
    var secondViewController: ProfileVC?

    private var activeViewController: UIViewController? {
        didSet {
            removeInactiveViewController(inactiveViewController: oldValue)
            updateActiveViewController()
        }
    }

    private func removeInactiveViewController(inactiveViewController: UIViewController?) {
        if let inActiveVC = inactiveViewController {
            // call before removing child view controller's view from hierarchy
            inActiveVC.willMove(toParentViewController: nil)

            inActiveVC.view.removeFromSuperview()

            // call after removing child view controller's view from hierarchy
            inActiveVC.removeFromParentViewController()
        }
    }

    private func updateActiveViewController() {
        if let activeVC = activeViewController {
            // call before adding child view controller's view as subview
            addChildViewController(activeVC)

            activeVC.view.frame = contentView.bounds
            contentView.addSubview(activeVC.view)

            // call before adding child view controller's view as subview
            activeVC.didMove(toParentViewController: self)
        }
    }

    // UI elements
    lazy var contentView: UIView = {
        let tv = UIView()
        tv.backgroundColor = UIColor.blue
        tv.translatesAutoresizingMaskIntoConstraints = false
        tv.layer.masksToBounds = true
        return tv
    }()

    var segmentedController: UISegmentedControl!

    override func viewDidLoad() {
        super.viewDidLoad()

        activeViewController = firstViewController

        checkIfUserIsLoggedIn()
        view.addSubview(contentView)       
        setupProfileScreen()

        let items = ["Travelers", "Me"]
        segmentedController = UISegmentedControl(items: items)
        navigationItem.titleView = segmentedController

        segmentedController.tintColor = UIColor.black
        segmentedController.selectedSegmentIndex = 0

        // Add function to handle Value Changed events
        segmentedController.addTarget(self, action: #selector(HomeController.segmentedValueChanged(_:)), for: .valueChanged)

        navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Sign Out", style: .plain, target: self, action: #selector(handleSignOut))
        navigationItem.leftBarButtonItem?.tintColor = UIColor.black

    }

    // reference to collectionViewController
    var travelersFeedVC: TravelersFeedVC!

    func segmentedValueChanged(_ sender:UISegmentedControl!)
    {
        switch segmentedController.selectedSegmentIndex {
        case 0:
            activeViewController = firstViewController

        case 1:
            activeViewController = secondViewController

        default: // Do nothing
            break
        }
    }

Solution

  • In your finishLoggingIn() it looks like you are using 2 separate HomeController instances.

    Something like this should fix it.

    func finishLoggingIn() {
        let rootViewController = UIApplication.shared.keyWindow?.rootViewController
        guard let mainNavigationController = rootViewController as? MainNavigationController else { return }
    
        // create it
        let homeController = HomeController()
        // use it
        mainNavigationController.viewControllers = [homeController] 
    
        let vc1 = TravelersFeedVC()
        let vc2 = ProfileVC()
    
        // use it again
        homeController.firstViewController = vc1
        homeController.secondViewController = vc2
    
        UserDefaults.standard.setIsLoggedIn(value: true)
        dismiss(animated: true, completion: nil)
    }