Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Attempt to present UINavigationController whose view

Tags:

ios

swift

AppDelegate

   initialViewController = storyboard.instantiateViewControllerWithIdentifier("LoginViewController")as! UIViewController
    }

    self.window?.rootViewController = initialViewController
    self.window?.makeKeyAndVisible()

Takes me to LoginViewController

let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("CardsNavController") as? UIViewController

self.presentViewController(vc!, animated: true, completion: nil)

I click on the login button takes me to CardsViewController

func goToProfile(button: UIBarButtonItem) {
   pageController.goToPreviousVC()

}

Clicking on back button which is part of the UINavigationController runs goToProfile (above) and takes me to ViewController.swift because that's where the pageController is declared.

let pageController = ViewController(transitionStyle: UIPageViewControllerTransitionStyle.Scroll, navigationOrientation: UIPageViewControllerNavigationOrientation.Horizontal, options: nil)

The error shows up here

func goToPreviousVC() {
    //let currentControllers = self.navigationController?.viewControllers
   if viewControllers.isEmpty   {

    setViewControllers([profileVC], direction: UIPageViewControllerNavigationDirection.Reverse, animated: true, completion: nil)
    pageController.presentViewController(profileVC, animated: true, completion: nil)
 else {
    let previousVC = pageViewController(self,  viewControllerBeforeViewController: viewControllers[0] as! UIViewController)!
    setViewControllers([previousVC], direction:  UIPageViewControllerNavigationDirection.Reverse, animated: true, completion: nil)
    }

Error:

Warning: Attempt to present <UINavigationController: 0x15ce314e0> on <MyApp.ViewController: 0x15cd2c6f0> whose view is not in the window hierarchy!

Basically the flow is like this AppDelegate -> LoginViewController -> ViewController and I need to get to ProfileViewController.

ProfileView has ProfileNavController while CardsView has CardsNavController

ViewController has a storyboardID of pageController.

Both ProfileView and and CardsView are embedded within UINavigationControllers (hence the NavController extension).

The second time I run the app after a fresh install it works perfectly (all the controllers get loaded okay). Should I push viewControllers in AppDelegate?

storybord

like image 909
Marin Avatar asked Mar 27 '26 07:03

Marin


1 Answers

I've checked your code using Xcode 7, which may not be ideal for resolving this issue because I had to covert your code to Swift 2.0, but here was what I found out.

ISSUE

  1. First time opening the app, this block:

    if currentUser() != nil {
        initialViewController = pageController
    }
    else {
        initialViewController = storyboard.instantiateViewControllerWithIdentifier("LoginViewController") as UIViewController
    }
    
    self.window?.rootViewController = initialViewController
    

Will initialize LoginViewController and make it the current window's rootViewController. At this point there is no pageController initialized

  1. When user taps on the button to go to the Profile screen, this method will be called

    func goToProfile(button: UIBarButtonItem) { pageController.goToPreviousVC() }

At this point, pageController is initialized, and off course, there is NOTHING in the viewControllers array. Let's see what happen in the goToPreviousVC method:

Original method looks like this:

    let nextVC = pageViewController(self, viewControllerAfterViewController: viewControllers[0] as UIViewController)!
    setViewControllers([nextVC], direction: UIPageViewControllerNavigationDirection.Forward, animated: true, completion: nil)

One thing you can see obviously is: calling viewControllers[0] could give you a crash because viewControllers is an empty array. If you use Swift 2.0, it doesn't even let you compile your code :)

SOLUTION

Let's go directly to the solution: Ensure that the pageController is available before trying to call it's viewControllers.

I blindly tried fixing you code in Swift 2.0 and found out that this method would work for you:

BEFORE: In LoginViewController.swift line 63

        let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("CardsNavController") as? UIViewController

        self.presentViewController(vc!, animated: true, completion: nil)

AFTER: Let's fix it like this

    let navc = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("CardsNavController") as! UINavigationController
    if let viewControllers = pageController.viewControllers where viewControllers.count == 0 {
        pageController.setViewControllers([navc.viewControllers[0]], direction: .Forward, animated: false, completion: nil)
    }
    self.presentViewController(pageController, animated: true, completion: nil)

It's working well here and probably I don't need to show you how the screen transition should look like :)

enter image description here

In case you would like to have the fixed source code as well, please find it HERE. Basically I converted your code to Swift 2.0 and ignored unnecessary parts like Facebook authentication for faster investigation.

Good luck & Happy coding!

like image 69
Ducky Avatar answered Mar 28 '26 20:03

Ducky