Is there a way to navigate from a UIView, which is part of a UIViewController, to another UIViewController neither using storyboards nor navigation controllers?
I have a UITapGestureRecognizer which I want to navigate to another UIViewController (BuyController()) whenever it is tapped once. Accordingly, my code looks like this:
//handle singleTap
func singleTapOnLikesDetected(_ sender: UITapGestureRecognizer) {
print("check") //works
let toController = BuyController()
BarController().pushViewController(toController, animated: true) //error occurs since BarController is no navigationcontroller
}
BarController (UITabBarController, UITabBarControllerDelegate) is my rootViewController in the AppDelegate.swift. The UIView however belongs to another UIViewController called HomeController (UIViewController, UIScrollViewDelegate). I do not use storyboards at all and I would like to keep it that way, as I am going to work on a project in a team (thus, a programmatic solution would be very welcome)...
You can simply do this to navigate, without using navigationController:
self.present(toController, animated: true, completion: nil)
Or Use this,
UIApplication.shared.keyWindow?.rootViewController?.present(toController, animated: true, completion: nil)
Only UIViewControllers can present other UIViewControllers not UIViews so your won't find the present method on the UIView. You also can't replace self with HomeController() because that creates a new HomeController with it's own UIView which hasn't been added to any other view (as the error suggests).
You have a couple of options really:
1) Create a protocol/delegate on your UIView and make the HomeController conform to it. Then the UIView can call the method in the protocol and the HomeController do the actual switching.
2) You could get the root view controller to present the new UIViewController like this: UIApplication.shared.keyWindow?.rootViewController?.present(viewController, animated: true, completion: nil)
Related
I am finding a very weird behaviour on my app, the structure of the view controllers is the following:
TabBarViewController contains:
- NavigationController(root is HomeViewController)
- NavigationController(root is SearchViewController)
- NavigationController(root is ProfileViewController)
When I am in HomeViewController, I present modally PremiumViewController
#objc func premiumTapped() {
let premiumViewController = PremiumViewController(viewModel: PremiumViewModel(networkService: NetworkService(), purchasesService: PurchasesService.shared))
navigationController?.present(premiumViewController, animated: true, completion: nil)
}
All fine until here.
Now I dismiss PremiumViewController.
dismiss(animated: true, completion: nil)
What happens is that HomeViewController calles it's init() and viewDidLoad()!
How is this possible? HomeViewController has been existing all this time, so why it calls init again?
Thanks to #rmaddy I found the issue.
To find it out, I put a breakpoint in the init() function that was called all the time. I found out that who was calling it was viewWillAppear() from the TabbarViewController.
The issue was that I initialise the tabbar view controller on the viewWillAppear() rather than on viewDidLoad(), which made recreate all the tab controllers every time one of the views appeared.
I am building a UIViewController (outer viewController) that contains another UIViewController (inner viewController). To do this I am using a container view. Now I want the inner viewController to push to another UIViewController, so I basically want the inner viewController to be a child of a UINavigationController. I know that you cannot change the content of a container view once it has been initialised (not directly anyways), so I've hit a wall.
Does anyone have any thoughts on how I can nest a UINavigationController inside a UIViewController or do I need to rethink my approach to the problem?
You can simply take a UINavigationController.
Initialise it with your InnerViewController.
Now, add the UINavigationController as (childViewController + addSubview + didMoveToParentViewController) in the OuterViewController.
Now as the InnerViewController is located inside a UINavigationController. You can push whatever you want on the UINavigationController.
Heavily inspired by #7vikram7 I ended up with a container view that embedded a UINavigationController which had my InnerViewController as its root viewController:
In my InnerViewController I am now able to push to any UIViewController, and control my navigation stack, for example:
override func viewDidLoad() {
super.viewDidLoad()
// Hide the navigation bar
self.navigationController?.setNavigationBarHidden(true, animated: false)
// Push to whatever viewController I want to
let viewController = self.storyboard?.instantiateViewControllerWithIdentifier("myAwesomeViewController") as! AwesomeViewController
self.navigationController?.pushViewController(viewController, animated: true)
}
I am having a problem with my project.
I created a custom transition from one VC to another. Worked fine, but my project is expanding and so I needed a navigation controller.
func itemButtonTapped(item: Item?) {
if let item = item {
let itemVC = storyboard!.instantiateViewControllerWithIdentifier("ItemViewController") as! ItemViewController
itemVC.item = item
itemVC.transitioningDelegate = self
//navigationController?.presentViewController(itemVC, animated: true, completion: nil) // #1
//navigationController?.pushViewController(itemVC, animated: true) //#2
}
}
The code above is meant to add viewController to existing navigation controller.
Option #1 -- it uses my custom transition and presents VC but it does not place it in the existing navigiationController
Option #2 -- it does not use my custom transition, but presents VC embeded in the existing navigationController
What should I do to combine these options, so I can show my VC using custom transition and add it to existing navigationController?
UINavigationController is a special case and you can't just use a transitioning delegate as you do for normal view controllers. Instead, you need to conform to the UINavigationControllerDelegate protocol and through those methods, provide the custom transition animation you want to implement.
Documentation here:
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UINavigationControllerDelegate_Protocol/
I have two view controllers and a button in my storyboard. I don't understand why this is not pushing the view to the second view controller. I have the Storyboard ID and Class of FirstViewController, so shouldn't it work? I keep getting this error Unknown class FirstViewController in Interface Builder file. What am I doing wrong?
#IBAction func button(sender: AnyObject) {
let viewController = self.storyboard?.instantiateViewControllerWithIdentifier("FirstViewController")
self.navigationController?.pushViewController(viewController!, animated: true)
}
Just to have an answer for others -->
Two ways to solve it ->
1.Add a navigationController in the storyboard, as you cannot do pushViewController on a viewController outside navigationController's stack.
2.You can push it as a modal view controller, using self.presentViewController(viewController, animated: true, completion: nil)
This will give a modal effect, and not the normal push effect.
I have a custom UIStoryboardSegue class, and I am trying to transition views from the navigation controller, so that my navigation bar stays intact.
Currently, I am presenting the to view controllers with:
self.sourceViewController.presentViewController(self.destinationViewController as! UIViewController, animated: false, completion: nil);
I would like to use:
self.navigationController?.pushViewController(self.destinationViewController as! UIViewController, animated: false);
However, I don't have access to the navigationController object within the Segue class. Is there any way I can access the navigationController variable from within this class?
Try this if your controller is the first, if not then change it accordingly.
let nc = self.navigationController!.viewControllers!.first as destinationViewController
self.navigationController?.pushViewController(nc, animated: false)