Is it possible to use segues in Views of ViewControllers instantiated with instantiateViewControllerWithIdentifier?
Here is a minimal example of what I'm trying to do (I need something like that in a biger project):
In the Main Storyboard I have a rootViewController, a secondViewController with the StoryboardID "secondViewControllerID" and a thirdViewController. The secondViewController is connected with the thirdViewController via button through a show-Segue.
In the class of the rootViewController I instantiate the secondViewController and set its view as subview of my rootViewController:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let storyboard = UIStoryboard(name: "Main", bundle: nil);
var vc = storyboard.instantiateViewControllerWithIdentifier("secondViewControllerID") as UIViewController
self.view.addSubview(vc.view)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
When I now execute the programm I see correctly the subview (secondViewController). But now the segue doesn't work anymore (when I click on the button I don't get to the thirdViewController)
Does anyone has an idea why?
(It is not possible in my project just to use the secondViewController without the instantiateViewControllerWithIdentifier because the secondViewController is part of a pageViewController that is managing its viewControllers via instantiateViewControllerWithIdentifier )
You are adding one view controller's view as a subview of another view controller. But by default, taps and gestures are handled by your main view controller and are not passed on to the subsidiary VC - hence your problem. There are some functions you need to call to get this to work. Take a look at the Apple Docs here.
Look at the section on "Creating Custom Container View Controllers". In summary, you need to wrap your addSubview method call with self.addChildViewController(vc) and vc.didMoveToParentViewController(self) function calls.
Related
I want to create a navigation hierarchy where I want to go to a SecondViewController from FirstViewController using a NavigationController. The FirstViewController contains a button B that I intend to use to go to a SecondViewController. I am employing the navigation controller concept since I want to return to the FirstViewController later. I have done the following steps to achieve it:
I have embedded a NavigationController into a storyboard X containing both the FirstViewController and SecondViewController.
I have created a segue from the button B present in the FirstView (has FirstViewController associated with it) to the SecondView (has SecondViewController).
I set the navigationViewController nvc as following after I have presented the firstViewController:
nvc = UINavigationController.init(rootViewController: firstViewController)
Later on, when the button B gets pressed, I execute the following code to push the secondViewController onto the navigation hierarchy:
self.nvc?.pushViewController(secondViewController, animated: true)
However, the secondView doesn't present itself even after pushing.
I have named the segue between the button B and secondViewController as kSegue, so I tried to perform the segue as an addition to see if the secondViewController presents itself or not:
self.performSegue(withIdentifier: "kSegue", sender: self)
An exception occurs when both the 4th and 5th steps are performed together. The exception states that I'm pushing a viewController that already exists on the navigation hierarchy, but the second view still doesn't open even if I comment the performSegue code.
May I ask what mistake I am making here?
In the storyboard, make sure there is a rootViewController segue from the navigation controller to the first view controller. Also, make sure the navigation controller is marked as the initial view controller.
In the code, change
self.nvc?.pushViewController...
To
self.navigationController?.pushViewController...
1) Take a navigation controller on your storyboard
2) Set navigation controller as initial view controller
3) set view controller A as a root view controller of your navigation controller
4) in "GoToB" method access View controller B's instance and push it in navigation controller
5) On View controller B's "Go Back" method write code to pop it.
6) Dont forget to set storyboard Id on both A & B view controller
class FirstViewController: UIViewController
{
lazy var secondViewController : SecondViewController? =
{
let secondViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "SecondViewController") as? SecondViewController
return secondViewController
}()
override func viewDidLoad()
{
super.viewDidLoad()
}
#IBAction func goToB(sender : UIButton)
{
guard let secondViewController = self.secondViewController else
{
return
}
self.navigationController?.pushViewController(secondViewController, animated: true)
}
}
class SecondViewController: UIViewController
{
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func goBack(sender : UIButton)
{
self.navigationController?.popViewController(animated: true)
}
}
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)
}
So, I have Navigation Controller. there are segue from Root View Controller to other View Controller.
When I want to get access to other View Controller I override prepareForSegue method and use destinationViewController property.
But that's not ok for me. All my stuff in prepareForSegue will be execute every time when segue is called, but I don't want it. Secondly, it destroys logic of my code: after performSegueWithIdentifier(actually before) execution jumps to other place in code.
It would be great if I can get access to other View Controller like I did it with Root ViewController - by keyword self, for example.
That's code example to make my question more clearer:
func startWorking() {
/*here we made some stuff for current VC
...
...
*/
//next we go to new View Controller
performSegueWithIdentifier("newVC", sender: nil)
//then all actions that I want to do begin at another method - prepareForSegue
//But I want get access to View Controller that user sees now!
//For example present some view:
let someView = UIView(frame: someFrame)
/*question subject*/.view.addSubview(somView)
}
/question subject/ - is the current ViewController that I have presented by segue and point of my question.
Sergey Gamayunov,
You can always access the top mostViewController in navigation stack using,
let viewCOntroller = self.navigationController?.topViewController
EDIT
I believe if you cant get your logic around the prepareForSegue or self.navigationController?.topViewController you must take a look into your design pattern :)
That being said I understand all you want to do is to access the ViewController after performSegue without using prepareForSegue, you can use this code
func navigationController(navigationController: UINavigationController, didShowViewController viewController: UIViewController, animated: Bool) {
if viewController is YourDestinationViewControllerClass {
print("You have access to viewController loaded do whatever you want")
}
}
The function stated above is a navigation controller delegate :) So you will have to declare your viewController to confirm UINavigationControllerDelegate. like
class ViewController: UIViewController,UINavigationControllerDelegate
and in
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.delegate = self
}
Thats it you are good to go :) Happy coding buddy :)
I have a view controller which is on my story board. The following is the code for view controller;
class SingleLineGraphController: UIViewController {
#IBOutlet var lineGraph: LineGraphView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// Mark: GraphDelegate implementation
func plotLineGraph(xAxisValue:[NSDate],yAxisValue:[Double],displayView:GraphDisplayView, graphTitle:String,graphStartDate:NSDate , graphEndDate:NSDate)
{
lineGraph.plotLineGraph(xAxisValue, yAxisValue: yAxisValue, displayView: displayView, graphTitle: graphTitle, graphStartDate: graphStartDate, graphEndDate: graphEndDate)
}
}
now i am accessing this view controller from another view like this;
let mainStoryboard = UIStoryboard(name: "Menu", bundle: NSBundle.mainBundle())
let singleLineGraphController : SingleLineGraphController = mainStoryboard.instantiateViewControllerWithIdentifier("SingleLineGraphController") as! SingleLineGraphController
let graphData = getGraphData(DashBoardRow.Respiratory, cellTitle: "Respiratory") as! LineGraphModel
singleLineGraphController.plotLineGraph(graphData.xAxisValue, yAxisValue: graphData.yAxisValue, displayView: graphDisplayView, graphTitle: graphData.cellTitle, graphStartDate: graphData.graphStartDate, graphEndDate: graphData.graphEndDate, latestReadingText: graphData.latestObservationText, latestReadingDate: graphData.latestObservationDate)
self.navigationController?.pushViewController(navigationController, animated: false)
The problem is when i instantiate the SingleLineGraphController from story board it doesn't call the viewdidload and hence the lineGraph becomes nil until the point where i call
singleLineGraphController.plotLineGraph(graphData.xAxisValue, yAxisValue: graphData.yAxisValue, displayView: graphDisplayView, graphTitle: graphData.cellTitle, graphStartDate: graphData.graphStartDate, graphEndDate: graphData.graphEndDate, latestReadingText: graphData.latestObservationText, latestReadingDate: graphData.latestObservationDate)
and hence it gives me an exception. I have commented out that line and put a breakpoint on viewdidload and find out that once the below line is executed , it loads the lineGraph.
self.navigationController?.pushViewController(navigationController, animated: false)
Does anyone have any idea how can i force the viewdidload before the above line so that my method doesn't crash.
In your line:
self.navigationController?.pushViewController(navigationController, animated: false)
You are pushing a navigationController, not your singleLineGraphController. Are you sure this is correct or did you leave some code out?
Even if you present the controller correctly, because you trying to plot your data before the view is presented the view is not accessible yet. One way to correct this is to set your data to plot on the controller as a property and only plot that data in your viewDidLoad when you know for sure the view is available.
You could access the view property to force the view to load before the controller is actually presented but this is a wrong solution to this problem.
if you will not push your VC to NavigationController, its viewDidLoad will not called
viewDidLoad will only called before coming on to the screen,
Then viewWillAppear and then viewDidAppear.
VC will only come on to the screen if you push or present or set it as rootVC of NVC and load that NVC as rootVC of window or set it directly to rootVC of window
Just call:
singleLineGraphController.view
After this line:
let singleLineGraphController : SingleLineGraphController = mainStoryboard.instantiateViewControllerWithIdentifier("SingleLineGraphController") as! SingleLineGraphController
I'm trying to create a custom segue. It doesn't work in the first time. I found a solution afterward by creating a new file extends UIStoryboardSegue and create a method called "perform". It works right now without using prepareSegue in ViewController. I'm copied my previous codes from preparedSegue to "Perform" func in new UIStoryboardSegue file. It print out the message but the delegate doesn't work.
View Controller
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Custom Segue
class CustomSegue: UIStoryboardSegue {
let transitionManager = TransitionManager()
override func perform() {
NSLog("Perform");
let toViewController = self.destinationViewController as UIViewController
toViewController.transitioningDelegate = self.transitionManager
}
}
I placed breakpoint in every func in Transition Manager, none of them execute and stop.
Segue settings:
Problem: TransitioningDelegate is not working
Full sources codes: here
The problem is that your perform implementation doesn't do anything:
override func perform() {
let toViewController = self.destinationViewController as UIViewController
toViewController.transitioningDelegate = self.transitionManager
}
All you do is create a view controller, give it a transitioning delegate, and then throw it away. You do not perform any transition! But that is exactly the job of a segue. It isn't clear what you can possibly be expecting to happen.
If the idea is that this is supposed to be a present (modal) segue, then you should make it a present (modal) segue in the storyboard, specify your custom segue class, and then, in your perform implementation, call super to do the actual presentation:
override func perform() {
let toViewController = self.destinationViewController as UIViewController
toViewController.transitioningDelegate = self.transitionManager
super.perform() // this will work, _if_ you have specified a present (modal) segue
}
Alternatively, your perform could perform the presentation itself, by calling presentViewController:... on the source view controller with the destination view controller as parameter.
But your perform does nothing. Nothing will come of nothing.