I am making an app that uses a UIPageViewController for navigation between pages. Everything works fine except that when I am trying to manually change which view controller is shown using setViewControllers it does not show.
I know that it is creating the view controller because it prints to the log, but the controller shown on screen isn't changed. The indexes I use for the subclass of uiviewcontroller that makes each view controller are also set correctly.
The function is called on the press of a view controller.
Here's the code that is supposed to change the controller:
func changeVC(VC: UIViewController) {
setViewControllers([VC], direction: .Forward, animated: true, completion: nil)
}
And the function call:
Let controller = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("contentView") as! ViewController
Controller.index = row
PageViewController().changeVC(controller)
That is inside of the didSelectRowAtIndexPath method of a table view.
Cjay was absolutely correct,
Lemme explain it little more in detail to help you understand what mistake you are doing.
Everytime you call PageViewController().changeVC(controller) new instance of PageViewController will be created and changeVC will be called on that new instance that you have just created. Though your code to instantiate a ViewController from storyboard setting it's index,calling changeVC and setting theViewController of PageViewController everything executes you won't see any of this making any effect on your screen because you changed the ViewController of the PageViewController which you just instiated which neither has a visible valid frame (0,0,0,0) nor it is loaded (not added to your ViewController's view) :)
Rather what you should have done is to do exact same things on a PageViewController which is already loaded :)
As per you comments you are loading the PageViewController from storyboard create an IBOutlet for it lets call it as myPageViewController :) so when you want to change the ViewController of it simply say,
let controller = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("contentView") as! ViewController
controller.index = row
self.myPageViewController.changeVC(controller)
Hope this helps :)
Related
I have two view controllers which are defined in storyboard as,
UIViewController -> (Show Detail Segue) -> UITabBarController
My problem is; I am using a library called netfox to debug my HTTP requests. And this librarie's UI is being triggered by Shake Gesture. But when I come to the UITabBarController Shake Gesture on the simulator does not work at first time. At the second time it dismisses the ViewController that is currently on the screen and obviously a child of UITabBarController and goes back to the initial UIViewController. This is exactly like connecting two ViewControllers with modal segue and calling self.dismiss() from the child one.
I tried to change rootViewController of the UIApplication by,
UIApplication.shared.keyWindow.rootViewController = self
in the viewDidLoad() method of the UITabBarController and it worked. However, for this solution, the items(buttons, titles) in the UINavigationBar of any UINavigationController that is the child of UITabBarController are missing.
I have no idea why this is happening. If someone helps me while I am solving this, I would be really appreciated.
Instead of using
self.performSegue(withIdentifier:_)
calling this in the UIViewController have worked:
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = mainStoryboard.instantiateViewControllerWithIdentifier("tabBarcontroller") as UITabBarController
UIApplication.sharedApplication().keyWindow?.rootViewController = viewController;
Respect to this answer.
I have a view controller (PhotoViewController) which has a child view controller (CameraViewController). In CameraViewController, there is a button that sets off a chain of segueing through other programmatically created view controllers. On the last of these view controllers, I want to unwind to the original PhotoViewController. All examples I have found require you to have view controllers in the storyboard, which I don't have. How can I do this?
You have not understood what an unwind segue is. It is merely a way of reversing the calls that created the current view controller and got it into the view controller hierarchy, in the special case where you want to trigger this through a segue in a storyboard.
For example, if you (or the storyboard) called pushViewController (for a pushed view controller), an unwind segue is just a way of calling popViewController.
Or, if you called (or the storyboard) called present (for a modally presented view controller), an unwind segue is just a way of calling dismiss.
If you don't have your view controllers in a storyboard, you don't need an unwind segue; you just do one of those two things, directly, in code.
Remember, we all got along for years just fine without unwind segues. In fact, we all got along for years without storyboards! And so can you.
If your original PhotoViewController is created on a storyboard and you want to segue back to it from a programmatically made VC you have present it rather than unwinding because unwindSegues are for storyboards only.
To present your PhotoViewController here is one of the ways to do so.
func presentPhotoViewController() {
let storyboard = UIStoryboard(name: "YourStoryBoardName", bundle: nil)
if let photoViewController = storyboard.instantiateViewController(withIdentifier: "PhotoViewControllerUniqueId") as? PhotoViewController {
// Pass any data you have (Optional)
self.present(photoViewController, animated: true, completion: nil)
// If your PhotoViewController is embeded in a navigation Controller do the following.
//let navController = UINavigationController(rootViewController: photoViewController)
//self.present(navController, animated: true, completion: nil)
}
}
In my app I have two View Controllers embedded in Navigation Controller. And I want to have an option to get data from the SecondVC while only the FirstVC is opened. So it's like I don't want a user to see (or at least interact) with the SecondVC.
The point is
So user just taps on the button on the FirstVC (without opening SecondVC before) and I can get access to the data on the SecondVC.
How can I get data from the next view controller (SecondVC) in a navigation controller without opening it? Or at least not letting user to interact with it.
For now my only ideas are:
To push segue when user taps a button, get it into memory, load data and then dismiss it. But that doesn't look good when view controllers change so fast.
To present next view controller modally offscreen, get data and dismiss it. So the user won't even see that another view controller opened.
Edit:
I need to get access not just to method or smth. I need to get to the #IBOutlet. To the subclass of UIView (working with CorePlot) to be exactly
You should never call loadView() directly.
Instantiate your UIViewController and then call loadViewIfNeeded().
Calling this method loads the view controller’s view from its storyboard file, or creates the view as needed based on the established rules.
EDIT:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
// make sure that the value is an optional so that you can unset it later. This one is optional because we use optional casting (with as?)
var vc = storyboard.instantiateViewController(withIdentifier: "secondVC") as? SecondViewController
vc?.loadViewIfNeeded()
// use the vc
// later unset vc variable
vc = nil
for this, you must call your 2ed View controller with 'modalPresent' and background of 2edVC must clear. Then get your data from 2ed VC. with no change. for more information about modalPresent there is the link.
Solution:
You can create a function for it or just insert in viewDidLoad() etc.
Don't forget to change StoryboardID for the chosen view controller (it can be done in your storyboard file in Identity inspector).
// You need to get to your Storyboard and instantiate the needed View Controller
let storyboard = UIStoryboard(name: "Main", bundle: nil)
var graphVC = storyboard.instantiateViewController(withIdentifier: "graphVCID") as? graphVC
// Then call loadViewIfNeeded (don't call loadView()) to load #IBOutlet's etc
graphVC?.loadViewIfNeeded()
// My outlets are loaded with data when these functions are called
graphVC?.viewDidLoad()
graphVC?.viewWillAppear(false)
// Unset the reference to VC
graphVC = nil
Ok, i know that when applicationWillEnterForeground fires from my AppDelegate that I can trigger events. What I would like to do is force the app to show a certain view when It re-appears from the background. The view is a UIViewController called loginViewController and it has a storyboard Id of "initViewController"
My question is, what do I use in this function (applicationWillEnterForground) to make this view load when the app comes into focus again? Thanks.
I've never done this, but you can probably accomplish it by adding the UIViewController to the UIViewController stack, if you just want to add a view controller and show its view (see Case 1) or by replacing the root view controller, if you want to discard the existing view controller stack and use a new one (see Case 2)
Case 1) In the first case you need a reference to the UIViewController that you want to make its parent view controller. You can save this in a static variable somewhere or if you are just planning on showing a temporary view when the app restarts then you can get a reference to the root view controller use it as the parent:
// get a reference to the main storyboard
let mainSB = UIStoryboard(name: "Main", bundle: nil)
// get a reference to the root view controller
if let rootVC = UIApplication.sharedApplication().keyWindow?.rootViewController,
// get a reference to the view controller using identifier
initVC = mainSB.instantiateViewControllerWithIdentifier("initViewController") as? UIViewController {
// present the view controller
rootVC.presentViewController(initVC, animated: false, completion: nil)
}
When you are finished use logic from within the view controller to dismiss it, and your user should be back where they started:
self.dismissViewControllerAnimated(true, completion: nil)
Case 2) You can discard the existing view controller stack and start a new one by replacing the root view controller and building up the stack manually. Present the view controllers in order with animated parameter set to false.
// get a reference to the main storyboard
let mainSB = UIStoryboard(name: "Main", bundle: nil)
// get references to view controllers
if let vc1 = mainSB.instantiateViewControllerWithIdentifier("vc1") as? UIViewController,
vc2 = mainSB.instantiateViewControllerWithIdentifier("vc2") as? UIViewController {
// set root view controller
UIApplication.sharedApplication().keyWindow?.rootViewController = vc1
// build up the view controller stack by adding next vc
vc1.presentViewController(vc2, animated: false, completion: nil)
}
Navigation Controllers
If one of your view controllers is a navigation controller you will need to cast it as such, and then push any view controllers onto your navigation controller. Navigation controllers have their own stack.
if let myNavCon = mainSB.instantiateViewControllerWithIdentifier("nav") as? UINavigationController {
// push view controller onto navigation controller stack
myNavCon.pushViewController(someViewController, animated: false )
}
WARNING
This does not deal with the model of your app at all (only the UI). You will also need to set any data that you would have set in prepareForSegue, etc. An easy system to use when you have VCs you present both programmatically and via storyboard segues is to take the code that would have been in prepare for segue and move it to your own instance method that takes a reference to the child view controller as its parameter. Then you can call it from prepare to segue with the destination view controller or from code before you present the view controller.
None of this code has been tested. It was written directly via the website. It likely contains typos. Please let me know so I can fix any.
I want to switch between different view controllers, here is my codes,
let sb = UIStoryboard(name:"Main", bundle: nil)
let vc = sb.instantiateViewControllerWithIdentifier("tabBarController") as ViewController
self.presentViewController(vc, animated: true, completion: nil)
'tabBarController' is the storyboad ID I had wrote in the identifier inspector.but I got some error at this line.
let vc = sb.instantiateViewControllerWithIdentifier("tabBarController") as ViewController
and here is the screenshot,
What's the problem?
And I have another question, here is the screenshot after the sb was initialized, the storyboardFileName is "Main.storyboardc", shouldn't it be "Main.storyboard"?
Thanks very much!!!!
The error is saying that the view controller returned by instantiateViewControllerWithIdentifier cannot be cast to a ViewController.
This is probably because you did not set the Custom Class property of your view controller in the storyboard to ViewController.
Go to your storyboard, select your view controller, then open the Identity inspector and look for the Custom Class property. Set it to ViewController and tab out of the field to make sure it takes. Then run again.