Im using StoryBoard and i am not so sure how to instantiate a ViewController or how to reference one.
The thing is i have two view controllers and i have one with a button. I want to go to the other view controller when i pressed the button from the first view controller.
I have tried something like that:
let secondViewController:UIViewController = UIViewController()
self.presentViewController(secondViewController, animated: true, completion: nil)
Does anyone knows how to explain it to me? Thanks!
There are couple of ways of navigating between view controllers. Here's how you do it in code without segues if you're going that way.
Presenting a view controller modally.
Say you have 2 view controller scenes in the storyboard, FirstViewController and SecondViewController. You want to transit from FirstViewController to SecondViewController and there is no segue between them.
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let secondViewController = storyboard.instantiateViewControllerWithIdentifier("SecondVC") as UIViewController
presentViewController(secondViewController, animated: true, completion: nil)
The important part is the that you have to assign an identifier to the view controller you want to go. In this case its the SecondViewController. The way you assign an identifier is you select the view controller, open up the right panel and in it, go to the Identity Inspector (the third one from the left) and under the Identity, assign an identifier to the Storyboard ID field. I put mine as SecondVC as you can see from the code snippet above.
Push to another view controller.
If you want to push on to another view controller instead of presenting it, all you have to do is embed the FirstViewController in a UINavigationController and change the code to this.
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let secondViewController = storyboard.instantiateViewControllerWithIdentifier("SecondVC") as UIViewController
navigationController?.pushViewController(secondViewController, animated: true)
In a comment of yours I saw you want to go to the next view controller based on a condition. Well all you have to do is check your condition in a if else statement and execute either of those above code.
Using Segues.
If you're going to use segues instead of code, here's how you do it.
In the storyboard first you select the button and Ctrl drag to the other view controller. You'll be prompted to choose between different segues. Select show for push or show detail for modal transition (I've explained what these are below). And that's it! If you run it and tap the button, you'd be taken to the other view controller.
But if you want more control over this, you have to do a little more work.. Instead of creating a segue directly from the button, select your view controller and select that little yellow icon on top (This is in Xcode 6. In older Xcode versions its under the view controller scene). Ctrl drag from that icon to the other view controller you want to transit to. You can see a connection appears between those two controllers. Select the segue and open up the right panel and go to the Attributes Inspector (The forth one from the left). Give a name to the field identifier. I gave ToSecond.
Now create a normal action from the button. And you have to call a method called performSegueWithIdentifier passing that identifier. What it does is basically execute a segue with the identifier we give.
#IBAction func segueButtonPressed(sender: UIButton) {
performSegueWithIdentifier("ToSecond", sender: nil)
}
And this would work. You can do the conditions checking here inside a if else and if the conditions are met, call performSegueWithIdentifier.
One other thing you're likely to face is having multiple buttons in a view controller and segueing to different view controller when you tap each of them. A method called prepareForSegue fires each time a segue happens. And inside it, you can check for current segue identifier and execute it. The below code snippet will make this clearer.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "ToSecond" {
let secondViewController = segue.destinationViewController as SecondViewController
}
}
Another thing I'd like to mention is that presentViewController and pushViewController are deprecated from iOS8. Instead you can use showDetailViewController for modal and showViewController to push.
showDetailViewController(secondViewController, sender: nil)
navigationController?.showViewController(secondViewController, sender: nil)
I'm not sure if these are backwards compatible. Meaning using show and show detail will work if you're developing for iOS 7 as well. If you are then just stick with the older methods.
First set seques of uistoryboardviewcontroller and try this
self.performSegueWithIdentifier("push", sender: self)
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if segue.identifier == "push" {
}
In the storyboard, select VC1 and then select the button and press control and while holding down the left button or touchpad drag across to your VC2. Then a menu should pop up. Select modal.
Run and Test. It should now perform a transition.
To transition back, the easiest way is to embed VC1 in a Navigation Controller. To do this, zoom out, select VC1 and go to the top of the screen and select:
Editor > Embed > Navigation View Controller.
Now test and run. You should have the option to go back.
If you are binding view controller programmatically you need to follow this step if you are creating storyboard based application.
It is similar what we don in Objective-c just the syntax has changed.
According to your question what you need to do is go to Main.storyboard and need to select identity inspector.
There you will be able to view identity which contains two fields
1.) Storyboard ID
2.) Restoration ID
Give them the name of view controller you have binded with class in storyboard id and select check box below restoration id. It will automatically copy storyboard ID in restoration ID.
You need to do same for all your view controllers.
let secondView = self.storyboard?.instantiateViewControllerWithIdentifier("SecondViewController") as SecondViewController
self.presentViewController(secondView, animated: true, completion: nil)
you need to write the name you have entered in Storyboard ID for self.storyboard?.instantiateViewControllerWithIdentifier("/* Storyboard ID */")
You can also visit the link:
Instantiate and Present a viewController in Swift
okay,
in interface builder control click on your button and drag the blue line that appears to the second view controller. The second view controller will highlight blue. You can release and the button is connected. In the popup menu select "modal segue". No code necessary. XCODE handles it.
Watch this video for a demo.
Related
I'm coding a simple app with swift and I'm stuck at the following point, I have two Controllers that lead to another one, and when I click on the cancel button, it always lead to the root Controller, no matter from where I come.
I have a first controller (UIViewController), that go to the Navigation Controller of my target Controller (the one from which I would like to go back to the right calling Controller).
I have a second controller (UITableViewController), which go directly to my target Controller.
Here's the code of my Cancel button:
// MARK: - Navigation
#IBAction func lendingCancelButton(_ sender: UIBarButtonItem) {
// Depending on style of presentation (modal or push presentation), this view controller needs to be dismissed in two different ways
let isPresentingInAddLendingMode = presentingViewController is UINavigationController
if isPresentingInAddLendingMode {
dismiss(animated: true, completion: nil)
} else if let owningNavigationController = navigationController {
owningNavigationController.popViewController(animated: true)
} else {
fatalError("the LendingViewController is not inside a navigation controller.")
}
}
If I correctly understood (you could then correct me if I'm wrong, I would learn something), it's testing if the ViewController that's presenting my target ViewController is a NavigationController.
So maybe that, as the second Controller (my UITableViewController) is not going through a NavigationController, so the last one calling my target view with a NavigationController is always the UIViewController.
Don't hesitate to tell me if it's not clear enough (too many times the word "Controller" in my post) or if you need additional code.
Try something like this
if let navigationController = presentingViewController as UINavigationController {
navigationController.popViewController(animated: true)
} else if let viewController = presentingViewController as UIViewController {
dismiss(animated: true, completion: nil)
} else {
fatalError("the LendingViewController is not inside a navigation controller.")
}
If i understood you want to use dismiss when you find a UIViewController and to pop the navigation when you find a UINavigationController right?
Ok so I finally found a way to make it working.
My tableViewController was embedded into a NavigationController. I removed it (since I could do without it, according to my need). From this View Controller, I draw a segue that "Show" my target view.
From my other ViewController (this one is embedded into a NavigationController), I draw a segue put that present modally my target view.
With the code provided in my initial post, it's working.
The only thing I didn't understand is why the NavigationController from my TableViewController was likely to cause it not working properly.
I have a UITabBarController which is connected to 3 UINavigationControllers. Each of these have a UIViewController as a root view controller. When I click on a button in one of these ViewControllers (VC1), I want it to segue to new ViewController (VC2). Simple enough.
During runtime, when the segue takes place and VC2 appears, the navigation bar disappears.
The navigation bar is present in the storyBoard / interface builder, and the segue is the type: present (Push).
Code in VC1:
#IBAction func create_clicked(_ sender: Any) {
performSegue(withIdentifier: "segueIdentifier", sender: self)
}
It might help to know how the UITabBarController tabBar is instantiated:
func login() {
let storyBoard = UIStoryBoard(name: "Main", bundle: nil)
let tabBar = storyBoard.instantiateViewController(withIdentifier: "tabBar")
window?.rootViewController = tabBar
}
Please help.
After a great deal of experimentation, we determined that even though Interface Builder said this was a Show (Push) segue, and even though we were in a Navigation Controller to start with,
it was behaving as a Present Modally segue.
So we changed the segue type, using the pop-up menu in Interface Builder, from Show (Push) to plain Push, even though that's deprecated — and the interface worked correctly.
And then we changed it back to Show (Push) and the interface continued to work correctly. Problem solved!
I suspect the storyboard was corrupted in some way. Obviously Interface Builder should not lie to you about what kind of segue this is, but basically, that is what it seems to have been doing.
EDIT The problem was caused, apparently, by making a Show Detail segue and changing it to Show. When you do that, it's still a Show Detail segue, which in this context behaves as a modal presentation. That's an Xcode bug! I reported it, and Apple now says this will be fixed in Xcode 9.3.
I have a simple app I am building. A list of items in 1 view controller with a table view and embedded navigation controller. When you select a row it brings you to the details screen (no problem).
Push from List View to List Item
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "ListItemDetailsVC", sender: nil)
}
Pop back from list item to List View
#IBAction func backToInboxTapped(_ sender: Any) {
navigationController?.popToRootViewController(animated: true)
}
This works fine!
My issue is that I have another view controller (add item) that presents modally when add button is clicked. The idea is that when it saves it brings you to the list detail vc.
#IBAction func saveItem(_ sender: Any) {
performSegue(withIdentifier: "DetailsFromAdd", sender: nil)
}
Opening up the details view controller from the add new controller works fine but once I open the details view I want to be able to go back to the rootViewController. The button does not work anymore
In your case you should not need to use segue. You have to clear some concepts.
You can't popToRootViewController or VC if you have pushed or show view controller from modally presented vc!!
Now if you want to achieve this then you have to make some changes that I am trying to mention below :
take one global variable or property (objective c concept) in your add item VC. now when you come to add item VC from lust VC set this global variable or property to self something like,
AddListItemVC *advc = [self.storyboard instantiateViewControllerWithIdentifier:#"addListItem"]; // addListItem is storyboard id for viewcontroller
[self presentViewController:advc animated:NO completion:^{
advc.vc = self; // here vc is the propery of type `UIVIewController` declare in AddListItemVC
}];
Now your code for going to details VC from add list VC should be like,
DetailListViewController *dvc = [self.storyboard instantiateViewControllerWithIdentifier:#"detailViewScreen"]; // detailViewScreen is storyboard id which you can set from identity inspector
[self dismissViewControllerAnimated:NO completion:^{
[self.vc.navigationController pushViewController:dvc animated:YES]; // here self.vc is global variable or property that contains reference of first VC (i.e. list view controller)
}];
no need to use performsegue in this case. You can use performsegue to go in detailvc directly from firstvc i mean from list vc.
Hope you got understand concept and i have written objective c snippet because of lake of time!! hope you can easily convert to swift!!
Firstly, As you say that addVC is modally presenting so make sure addVC have some navigation otherwise you'll not able to push listItemVC from addVC.
Now second thing, when you're modally presenting addVC then push listItemVC pushed from addVC, now addVC navigation controller have two view controllers. When you call navigationController?.popToRootViewController(animated: true) this will bring you to addVC because is being presented thats's why you will not able to see listVC.
To solve this you've to add some dismiss presented view controller whenever you'll arrive at addVC.
My issue is that I have two view controllers, and I need to transfer a string from one view controller to the next. I already have that down from a segue. The actual issue is that I need a label to change in the next view controller to make it match the currentTitle of the button in the previous view. And yes, there are multiple buttons.
My code from view 1 is this:
#IBAction func whichButtonWasClicked(sender: AnyObject) {
clickedButton = sender.currentTitle!!
}
// Segue - Pass the Data of which lesson was chosen
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let DestViewController : Lesson = segue.destinationViewController as! Lesson
DestViewController.lessonPlan = clickedButton
}
I connected all my 7 buttons to that same one action.
The problem is that when I transfer view controllers, and do...
print(lessonPlan)
It does not give me the title of the button from the action, because I guess that action is after the segue. And yes I tested it, and it IS after the segue. SO it does work, it's just a bit delayed, and I need it to be a part of the segue.
Since your IBAction is being called after prepareForSegue, you can use programmatic segues.
Right now, you're probably dragging a segue from your button to the next view controller. Instead, you can drag a segue from the current view controller to the next one (see this answer for a visual guide). This will create a programmatic segue that you can trigger whenever you'd like from code. Remember to give the segue an identifier in Xcode.
Then in whichButtonWasClicked, call self.performSegueWithIdentifier("identifier you setin Xcode", sender: sender) after you set clickedButton in order to trigger the segue. By doing so, you'll ensure that your button action is called before the segue starts.
I created a table view and from there let say a user pressed a cell it will go to the detailItemView , but the only problem right now is that whenever a user is in detailItemView there is no back button even thought I already embed a navigation Controller
Here's the code that will perform a segue once a user pressed a cell:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using [segue destinationViewController].
var nav = segue.destinationViewController as! UINavigationController
var detailScene = nav.topViewController as! DetailViewController
// Pass the selected object to the destination view controller.
if let indexPath = self.tableView.indexPathForSelectedRow() {
let row = Int(indexPath.row)
detailScene.currentObject = objects?[row] as? PFObject
}
}
The screenshot for the storyboard:
edited Screenshot
I tried to do the answer below but there is still no back button
Another screenshot, the detailView controller doesn't have a back button
Embed the TableViewController inside the NavigationController. Not the DetailViewController directly. Check out the screenshot.
Make sure, that the segue, connecting your TableViewController and your DetailViewController is of type Show (e.g. Push). You have to literally push a new View on top of the Navigationstack.
I've had the same issue just now, resolved by deleting segue, adding again and giving it an identifier straight away.
Click on the StoryBoardSegue, the link between the tableviewcontroller and the detailviewcontroller. In the attributes inspector you may need to click on the "Animates" check even if it is checked. Do this after selecting "Show(e.g.Push)" located in the "Kind" drop down menu. Then run your app and then reselect the same "Animates" option after stopping your app. I think this is a bug and it took my an hour to figure out how to solve this. Additionally, you can use the deprecated "Push" but this will illicit a warning. Deprecated "Push" is under "Kind" in the StoryBoardSegue attributes inspector.
I was struggling with this also, and I named the Identifier of the segue, and now it works. Just select the segue (the arrow) from the Table View to the View , then go to the right top side, where the options are, and select Show the attributes inspector. There is a field for the Identifier.
Just make uinavigation with UItableview then connect the Cell as ( show )
If your current view controller is embedded in a navigation controller, you don't need to embed the detail view controller in a navigation controller. Just connect a button to the detail view controller and the back navigation will come automatically.
change "Interface style" to "Inferred" of detailView in Attributes inspector