So, I created a login page with basic logic. If username and/or password are empty, it displays an error message. If they're full, then it transitions to the next page (home). I created a second ViewController on the storyboard, and created a SecondViewController". I then defined the respective ViewController class to "SecondViewController".
On the first ViewController.swift file, i used this code:
func transition(Sender:UIButton!)
{
let secondViewController: SecondViewController = SecondViewController()
self.presentViewController(secondViewController, animated:true, completion:nil)
}
When I tested it out however, pressing the login button (when both the username and password are filled) transfers me to a black screen instead of the second View Controller. Any ideas on what to do?
If you want the view to contain things added to it on the storyboard, you could give it a Storyboard ID (a couple of boxes below where you set the class of the view controller in the storyboard).
If you give it for example the ID "secondVC" you can then create it using the following:
let secondViewController = storyboard?.instantiateViewControllerWithIdentifier("secondVC") as? SecondViewController
You can't initialize a view controller with just the default init method.
You should probably create a view controller scene in your storyboard, add a unique identifier on it, instantiate it using instantiateViewControllerWithIdentifier:, and then display it like you are doing.
You'll want to make a segue between the view controllers in your Storyboard, and call self.performSegueWithIdentifier().
Or you can give your `UIViewController an identifier and present it programmatically too:
let storyboard = UIStoryboard(name: "MyStoryboardName", bundle: nil)
let secondViewController storyboard.instantiateViewControllerWithIdentifier("") as? SecondViewController
if let secondViewController = secondViewController {
self.presentViewController(secondViewController, animated:true, completion:nil)
}
There are basically three ways to do this
1)Creating segue in storyboard,though it will only work if you have single segue from that item
2)using prepareforSegue and performSegue Methods in your presenting view controller
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "YourIdentifier" {
let controller = segue.destinationViewController as!secondViewController
}
}
Then use this in your IBACtion
performSegueWithIdentifier("YourIdentifier", sender:sender)
3)(Recommended One)
let controller = self.storyboard?.instantiateViewControllerWithIdentifier("ContactsVC")! as! ContactsVC
self.presentViewController(controller, animated:true, completion:nil
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 have three viewControllers Main, A, B. Main ViewController holds ContainerView and other content as well does all transactions in containerView. ViewControllerA has ButtonA when pressing it content of container has to change to ViewControllerB
how can I do that? I cannot find any similar examples.
You will need to create create delegate for that.
First create a protocol
protocol ViewControllerADelagate {
func didPressOnButtonA()
}
In ViewControllerA add following delegate variable
class ViewControllerA {
....
var delegate : ViewControllerADelagate?
....
}
In ViewControllerA add following on button press
#IBAction buttonAPressed(sender : UIButton) {
self.delegate?.didPressOnButtonA()
}
In MainViewController assign the delegate of ViewControllerA to self
like
vcA.delegate = self
Implement the delegate method in MainViewController like
func didPressOnButtonA {
let storyboard : UIStoryboard = UIStoryboard(name: storyboard, bundle: nil)
let vcB : UIViewController = storyboard.instantiateViewController(withIdentifier: viewControllerIdentifier) as! ViewControllerB
self.addChildViewController(vcB)
self.containerView.addSubview(vcB.view)
vcB.didMove(toParentViewController: self)
vcB.view.frame = CGRect.init(x: 0, y: 0, width: self.containerView.frame.size.width, height: self.containerView.frame.size.height)
}
While click on ButtonA. Post a notification to mainView. There remove viewController A from Container and add View Controller B .
I have created a storybaord with sample. you can download it from here.
You need to change the embed view to a navigation controller and then you can use segue to show second view on button press. also hide/show navigation bar depends on requirement.
I am not fond of using these but you can get the child view controller from parent controller by accessing an array childViewControllers. On view did load you would need to go through all of these and find the one that can be typecast into your view controller type like
childViewControllers.flatMap { $0 as? AViewController }.first.
Now that you found the correct view controller I suggest you to either assign yourself as a delegate
childViewControllers.flatMap { $0 as? AViewController }.first.delegate = self
or simply add a button target
childViewControllers.flatMap { $0 as? AViewController }.first.button.addTarget...
Now this can easily be done if you simply embed the 2 view controllers at the same time (have 2 content views) and hide one or the other depending on which you show. At least this way you can assign connection straight away. When this is not the case then you will need to iterate again when setting a new controller or assign connections where you initialize a new view controller.
In this case it then seems better to turn the system around: When child view controller is loaded rather call
self.delegate = parentViewController as? AViewControllerDelegate
Although this will work it seems wrong that a child view controller will control who its listener is so I advise you to avoid such coding.
I in general use a custom implementation for container view which you could do the same or maybe at least subclass the native one and target the methods so that your code will look something like:
private onBPressed() {
containerView.setViewController(viewController: BViewController(delegate: self), animation: .fade)
}
When you pressing Button A from View A:
#IBAction func BtnAPress(_ sender: Any)
{
//Moving Storyboard
let Storyboard = UIStoryboard(name: "Main", bundle: nil)
let MainVC : UIViewController = Storyboard.instantiateViewController(withIdentifier: "ViewControllerB")
self.present(MainVC, animated: true, completion: nil)
}
I have two view controllers in my storyboard: view1 and view2.
In view1, I used the storyboard to make a segue from its tableview to view2.
But now in view2, I have programmatically created a button, and it is supposed to segue to another instantiated view2.
When the button is clicked, I made a function to execute:
func buttonPressed(sender: UIButton){
//TODO:- GET NEXT PAGE
self.performSegueWithIdentifier("anotherView2", sender: sender)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "anotherView2"){
let anotherView2Controller = segue.destinationViewController as! View2
...
...
}
}
Of course it does not work, since there is no segue identifier called anotherView2. So I want to make it somehow, but I really cannot find good way to manage this.
Is there anyway to create the segue programmatically? I tried to make the segue from storyboard, but since the button is created programatically, I cannot segue to view2 to view2.
You want to use presentViewController instead of performSegue:
// however you want to initialize the new vc
let vc2 : View2 = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("view2.id") as! View2
an alternate way to init the vc is to do this:
let vc2 = View2()
and then set the data and present:
vc2.index = index // whatever you want to pass
self.presentViewController(vc2, animated: true, completion: nil)
Otherwise you could create a new view controller in your storyboard, but I wouldn't recommend it.
I have a ViewController that I want to present when a push notification is opened from the locked screen.
The view controller is embedded inside a chain of view controllers, the chain is:
TabbedBarController(root) -> NavigationController0 -> ViewController0 -> ViewController1 -> ViewControllerIWant
I have two variables from the push notification I want to pass into ViewControllerIWant
So far I have:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let rootVC = storyboard.instantiateViewControllerWithIdentifier("HomeVC") as! UITabBarController // HomeVC is the TabbedBarController
let NaviVC = storyboard.instantiateViewControllerWithIdentifier("NavigationController0") as! UINavigationController // NavigationController0 is the Navigation Controller for this stack
let ViewController0 = storyBoard.instantiateViewControllerWithIdentifier("ViewController0") as ViewController0
let ViewController1 = storyBoard.instantiateViewControllerWithIdentifier("ViewController1") as ViewController1
let ViewControllerIWant = storyBoard.instantiateViewControllerWithIdentifier("ViewControllerIWant") as ViewControllerIWant
// Pass the Variables into ViewControllerIWant
ViewControllerIWant.Variable0 = "This is the first Variable"
ViewControllerIWant.Variable1 = "this is the second variable"
// Load the VC
self.presentViewController(ViewControllerIWant, animated:true, completion:nil)
I can load ViewControllerIWant directly but then loose the navigation and tab controllers so moving backwards requires you yo close the app and reload.
How do I nest the VC's so that when the user swipes this notification it loads ViewControllerIWant ?
You can just set the viewControllers of the navigationController. And show the current navigationController.
In your example you should add this after the line will be (I have change all to a lowercase. You really shouldn't be using a uppercase as variable)
viewControllerIWant.variable1 = "this is the second variable"
naviVC.viewControllers = [viewController0, viewController1, viewControllerIWant]
You can keep your navigation controller by using segues instead of presenting the View Controller
Once the segue is created from your starting VC into your desired VC, you give it an identifier (e.g. "segueFromVC1ToVCIWant").
Then call the method as an action of your notification:
self.performSegueWithIdentifier("segueFromVC1ToVCIWant", sender: nil)
Finally can pass the variables like this:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "segueFromVC1ToVCIWant"){
let viewControllerIWant = segue.destinationViewController as! ViewControllerIWant
ViewControllerIWant.variable0 = self.variable0
ViewControllerIWant.variable1 = self.variable1
}
}
Make sure you create variables with the same name in ViewControllerIWant in order to receive the values.
I have a sent Action, as follows:
#IBAction func showSettings(sender: AnyObject) {
let settingsPicker = SettingsViewController()
settingsPicker.setDelegate(self)
let navigationController = UINavigationController (rootViewController: settingsPicker)
self.presentViewController(navigationController, animated: true, completion: nil)
}
The method creates a controller, sets a reference to the delegate, and creates a navigation controller.
All this works, however the widgets defined in the story board do not appear. The SettingsViewController should manage a ui which is defined in a story board. I presume becuase I create it programmatically none of the widgets appear. The SettingsViewController does not programmatically create widgets, the are declaratively defined in the story board.
If I link (in the storyboard) the two controllers with a segue, then the widgets appear, but my action is not being used.
How can I use my action and present the view controller / ui as defined in the storyboard?
When you create a segue between your UIViewControllers, you should define an identifier, eg: "settingsSegue".
In your code you can then perform that segue by calling the segue with the identifier:
#IBAction func showSetting(sender: AnyObject) {
performSegueWithIdentifier("settingsSegue", sender: nil)
}
To set up the SettingsViewController you should implement the following:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?){
if let settingsController = segue.destinationViewController as? SettingsViewController {
settingsController.delegate = self
}
}
Interacting with Storyboard and Segues
If you want to invoke a segue through code, see Laffen's answer.
If you want to create a view controller that's defined in your storyboard and then display it programmatically, use instantiateViewControllerWithIdentifier to create a new instance of your view controller, then display it to the screen as desired (present it modally, push it onto your navigation stack, or whatever you want to do.)