Using Tab Bar Controller and Navigation Controller - ios

I want to navigate between view controllers as it is shown. I do not want to use segues.
When I try to navigate form FirstViewController to SecondViewController by clicking a button:
let next = self.storyboard?.instantiateViewControllerWithIdentifier("secondViewController") as! SecondViewController
self.presentViewController(next, animated: true, completion: nil)
I get:
Warning: Attempt to present SecondViewController on FirstViewController whose view is not in the window hierarchy!
Any ideas how to insert Navigation Controllers in-between?
Edit: It is TabBarController not TabViewController on the figure.
I navigate from First Scene's controller named "A-Controller" to First View Controller with :
#IBAction func navigateToFirstViewController(sender: AnyObject) {
let next = self.storyboard?.instantiateViewControllerWithIdentifier("firstViewController") as! FirstViewController
self.presentViewController(next, animated: true, completion: nil)
}
It is not shown on the figure.

If you try to present modal view over TabBarController, then :
let next = self.storyboard?.instantiateViewControllerWithIdentifier("secondViewController") as! UIViewController
self.tabBarController.presentViewController(next, animated: true, completion: nil)

Warning: Attempt to present SecondViewController on FirstViewController whose view is not in the window hierarchy!
Turns out this error meant I have to do the navigation from First View to Second View after my First View appears and is ready.
Putting the navigation lines inside viewDidAppear() function solved the issue.

Related

How can I set back to first view controller by navigation controller in code in present without segue?

I have in my project tow view controller and I linked the first VC with navigation controller but the problem is : I used present to go to second VC (that mean I didn't use segue) ... how can I set back to first VC in navigation Controller by code (without segue) .
picture from my storyboard
my code :
let storyboard = self.storyboard
let viewcontroller = storyboard?.instantiateViewController(withIdentifier: "contact_detail") as! ViewController2
viewcontroller.arr2 = arr
present(viewcontroller, animated: true, completion: nil)
With the dismiss method it will work.
Dismisses the view controller that was presented modally by the view controller. (Apple Docs)
self.dismiss(animated: true, completion: nil)
If you are presenting a viewcontroller with present method, you can dismiss it with dismiss method.
If you are adding any view controller with push method then only it will get added to your navigation stack and you can remove it by calling popviewcontroller on it's back action.
Here, you are presenting a viewcontroller, hence it will not get added to your first navigation stack and you can not remove it with pop action on click of back.
If you are looking for a back button like feature on presented view controller, you can add a back button in toolbar, dismiss a viewcontroller on back button action, and can animate it like a popnavigation while dismissing.
extension UINavigationController {
public func removeViewController(classes : [String]) {
var vcs = [UIViewControllers]()
for viewController in self.viewControllers {
let name = viewController.className
if !classes.contains(name) {
vcs.append(viewController)
}
}
if classes.count < vcs.count {
self.viewControllers = vcs
}
}
}
now think you have 4 viewControllers , A, B, C, D, you want to remove B and C and Move Back To A
In D's View Controller
override func viewDidLoad() {
super.viewDidLoad()
//your works
let viewControllersToRemove = [String(describing: type(of:B)), String(describing: type(of:C))]
navigationController.removeViewControoler(classes : viewControllersToRemove)
}
I solved this by using pushViewController
self.navigationController?.pushViewController(MyViewController, animated: true)

How to dismiss a viewController to rootViewController without showing any VCs in between dismiss process?

Let say I have the following VCs:
RootVC --> VC A --> VC B
I'm using present method to present view controller from RootVC to VC A then to VC B. Now I'm on VC B and I want to dismiss from VC B back to RootVC using
self.view.window!.rootViewController?.dismiss(animated: true, completion: nil)
it works but I still see VC A shows up during the dismiss process. Then, I try this method
self.presentationController?.presentedViewController.dismiss(animated: true, completion: nil)
It also works to dismiss back to root VC but I still see VC A in process.
My question is is there a way to not show VC A during the dismiss process? I already try animated: false but still get the same result. Thanks!
You need to make the change in the modalPresentationStyle to the .custom. The custom will allow you to view the presentingViewController view when the current visible controller's view is transparent.
Now when you want to go back to root view on the current presenting stack you need to call the method dismissToRootViewController(animated: completion:).
In the implementation of this method will allow all intermediate presenting view controller view to be transparent which will give you dismiss animation from VC c to RootVC.
extension UIViewController {
func dismissToRootViewController(animated: Bool, completion: (() -> Swift.Void)? = nil) {
var viewController = self.presentingViewController
while viewController?.presentingViewController != nil {
viewController?.view.alpha = 0.0
viewController = viewController?.presentingViewController
}
self.dismiss(animated: true) {
viewController?.dismiss(animated: false, completion: completion)
}
}
}
You can try to use this:
navigationController?.popToRootViewController(animated: false)

I got this warning "attempt to present ViewController whose view is not in the window hierarchy" when I try to present a view

My view hierarchy looks like image below:
My problem is I want to show VC1 when I click any button on VC3. Here is my code
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("registerNavigation")
self.presentViewController(vc, animated: false, completion: nil)
but I got this warning "Warning: attempt to present ViewController whose view is not in the window hierarchy" and nothing happen. Please tell me, what did I do wrong?
If you want to show VC1 from VC3 the you not need to present it again because it is already loaded in navigation stack. you just need to dismiss or pop VC3 and VC2 from navigation stack. If you have presented it then dismiss and if you have pushed it then popped it.
Your warning's meaning : you are trying to presenting which is in the view hierarchy of navigation controller 1 but not in the view hierarchy of navigation controller 2.!!
Hope this will help :)
You are presenting to navigationController so use code like below.
let VC1 = self.storyboard!.instantiateViewControllerWithIdentifier("registerNavigation") as! ViewController
let navController = UINavigationController(rootViewController: VC1)
self.presentViewController(navController, animated:true, completion: nil)

Whose view is not in the window hierarchy after dismissViewController

I have a view controller that shows the details of an object. On this view controller is an "edit" button which shows modally the edition view controller. When I try to dismiss the modally presented view (edit view controller) :
self.dismissViewControllerAnimated(true, completion: nil)
I get the following error and it's presenting my initial viewController instead :
Warning: Attempt to present ≤Deevent.MyEventsVC: 0x7f99b70160a0≥ on ≤Deevent.EventCreationVC: 0x7f99b7238690≥ whose view is not in the window hierarchy!
So what I've tried was to set the root view controller of my view to the view I wanted to go back and present it in the completition of my dismiss. It's working well, but my application is in a Tabbar Controller and now it's not in it anymore. Same for the navigation controller.
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("MyEventsStoryboard") as! MyEventsVC
let appDelegate = (UIApplication.sharedApplication().delegate as AppDelegate)
appDelegate.window?.rootViewController = vc
self.dismissViewControllerAnimated(false, completion: {
self.presentViewController(vc, animated: true, completion: nil)
})
Is there an other approach for presenting viewControllers after dismiss without leaving the Tabbar controller ?
Thanks
Since you are trying to present the new view controller by calling self.presentViewController after you dismiss self, you get the error. I can offer you a solution if you are using a navigation controller.

pass data via. navigationController

i'm trying to pass Data to a viewController. The problem is that its embedded in a navigationController and is presented modally. How can i pass to a viewController which is presented modally and is embedded in a navigation Controller
func offerNew(sender: UIBarButtonItem) {
let offerVC = self.storyboard?.instantiateViewControllerWithIdentifier("OfferViewController") as UINavigationController
self.presentViewController(offerVC, animated: true, completion: nil);
}
i've tried this
let offerVC = self.storyboard?.instantiateViewControllerWithIdentifier("offerNavigation") as UINavigationController
let targetVC = offerVC.topViewController as OfferViewController
self.presentViewController(targetVC, animated: true, completion: nil);
I'm assuming that offerVC is the navigationController in which your "target" view controller is embedded. If so, your target view controller can be accessed through the topViewController property of offerVC. So
let targetVC = offerVC.topViewController as TargetViewController
will give you a reference. You can then access the properties of your target view controller.
EDIT
But you should present OfferVC - it will display targetVC automatically.
self.presentViewController(offerVC, animated: true, completion: nil);
You could add a property to your OfferViewController, then assign it. Your view initialization code would be done in loadView, which won't be called until after presentViewController, so not having it during init shouldn't be a problem in most cases.

Resources