I have the following scenario: I have a view controller and want to navigate programmatically to another view f.e. to give the user the ability to change settings.
So I have VC1 and VC_settings and a present modally segue between those controllers. I gave the segue an identifier and call it like this:
performSegue(withIdentifier: "SegueToSettingsView)
Ok, that works without any problems. The settings view is opened and I can navigate back to VC1.
Now I want to have the same functionality in another view controller VC2. I want to reuse the settings view but the problem is that the segue is already connected between VC1 and VC_settings and I can't connect a new one for VC2.
And if I try to call the existing segue with the coce above in VC2 then the app crashes.
In swift it is possible to create segues from multiple views two one viewcontroller. Here I have set it so there are segues from VC1 to Settings and VC2 to settings which should work.
Hope it helped
Since you have VC1 and VC_settings connected with segue named "SegueToSettingsView" also the perform selector is working fine.
As per your need make another segue from VC2 to VC_Settings and give a different name for it. then make use of below code to perform segue.
performSegue(withIdentifier: "SegueToSettingsViewFromVC2")
Steps to make segue:
Then select the connection and set identifier
Related
Summary:
I'm writing a Swift iOS app with a login screen and several other views in a tab view controller. I'm transitioning from one viewcontroller to another via the "control" + left click -> "Show" method. I want to make sure I'm not designing my iOS app incorrectly with memory leaks or other flaws.
Relative Questions:
Does this mean a new view of that ViewController is created each time "Show" is called?
Could this cause a memory leak or the app to crash?
Do I need to unwind the ViewControllers at some point?
What is the best way to unwind a ViewController when launching another ViewController?
It seems what you are talking about is manually creating a Show Segue (a transition, made via the Storyboard with ctrl + click and drag to another ViewController). This is one correct way to create a Segue (transition) from one ViewController to another. To utilize this, you will need to use the left panel on the Storyboard, give this segue an identifier, and use this identifier to preform a segue from the first ViewController to the second in some sort of method or action (like a button click, etc) using the performSegue method:
self.performSegue(withIdentifier: "NameOfSegue", sender: self)
Here's more info on segues from the docs:
https://developer.apple.com/library/content/featuredarticles/ViewControllerPGforiPhoneOS/UsingSegues.html
You can read even more detail in the "Modifying a Segue’s Behavior at Runtime" section. Here's a quote: "Most of the work happens in the presenting view controller, which manages the transition to the new view controller. The configuration of the new view controller follows essentially the same process as when you create the view controller yourself and present it." Memory leaks shouldn't be an issue here. Unwind segues let you dismiss view controllers that have been presented, but they are not always needed.
I have 4 ViewControllers in my storyboard. I want all of them to be able to access my "Settings" ViewController by performSegue.
Is it possible to have ONE segue to perform this, instead of ctrl + drag from each and every ViewController to my "Settings" ViewController?
No its not possible with a single segue. You need 4 different segues from 4 different ViewControllers. But you can do this programatically.
Make an extension for UIVIewController
extension UIViewController
{
func showSettingsScreen()
{
let storyBoard = UIStoryboard(name: "YourStoryBoardName", bundle:nil)
let settingsScreen = storyBoard.instantiateViewControllerWithIdentifier("YourSettingsViewControllerID")
self.navigationController?.pushViewController(settingsScreen, animated: true)
}
}
Now you can call showSettingsScreen() from any of your view controllers(Make sure this view controller has a navigation controller).
You cannot do that. A segue has only one source and one destination. You could programatically instantiate your Settings ViewController and display it either by using push or by using present. What you should think of though is why do you have to go to settings from so many places. It might be a sign of bad design and duplicate code. Usually applications have only one such button/action that can be accessed from multiple screens (by using some kind of container view implementation) or from only one screen.
I really dont think so there is a way to do so. U ought to connect ur SettingsViewController to all of your 4 View Controllers, add segue , and define a segue identifier which is used in
prepareForSegue:sender:
or
shouldPerformSegueWithIdentifier:sender:
methods. U can access segues through these identifiers. If u find "ctrl + drag from each and every ViewController to "Settings" ViewController " tasky you can opt for Navigation Controller as well. U just have to embed Navigation Controller in your storyboard and define Storyboard Id for every View Controller and you are done. Just use storyboard id of view controller to be instantiated and u good to go.
ViewController *vc = [self.storyboard instantiateViewControllerWithIdentifier:#"ViewController"];
[self.navigationController pushViewController:vc animated:YES];
Apart for assigning storyboard id you dont have to worry about storyboard ,No ctrl+drag thing.
I thought of one more elegant solution i.e. using Container View. You can take button to switch, SettingsViewController as common, in your Container View Controller while displaying every ViewController.
happy Coding..
There is one way to do this. Create a root view controller and matching view which contains a single embedded view. Add your segue to this root controller. Then in your app you switch in the other view controllers using standard child container techniques. This is pretty much the concept that UINavigationControllers use.
One advantage from this is that if you want to have common elements which are visible to all controllers then you can add them to your root controller.
But it all depends on what you are trying to achieve.
I have a project in which I have a storyboard. The storyboard contains several view controllers. I'll simplify it and omit the unnecessary details. Within a Navigation Controller, there are several UIViewControllers hooked up with segues. (see screenshot below...)
From left to right on the bottom row it goes Navigation Controller ➞ VC1 ➞ VC2. VC1 also has a manual segue to VC3 on the top row which goes VC3 ➞ VC4. VC1 is the starting view controller of the app. There two segues connecting from VC1. The segue from VC1 ➞ VC2 is the primary segue and from VC1 ➞ VC3 is the secondary. The secondary segue will be called programmatically when the user runs the app for the first time as a sort of welcome/getting started sequence.
I set this up going off of this post here, referencing the second most upvoted post in which the user suggests a segue between the starting VC file itself and the view of the other VC and then calling performSegueWithIdentifier.... here is the problem.
When running this app, there are no compile-time errors. It all looks good. It loads up, and I have this set to go in the view did load, which for now I am forcing it to run as if it were the first time like so...
override func viewDidLoad() {
super.viewDidLoad()
let firstVisit = true
if firstVisit == true {
self.performSegueWithIdentifier("welcomeSegue", sender: self)
}
// Do any additional setup after loading the view.
}
So it loads up. I can tell it attempts to show VC1 before VC3 promptly gets pushed on top as desired. VC3 is displayed, brilliant. The issue occurs when I press the one and only button on VC3 that should perform a typical segue from VC3 ➞ VC4. This is when I get the following error:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Receiver (<X.VC1>) has no segue with identifier 'welcomeSegue''
Now the strange thing to me that I cannot figure out is that, even though VC3 has been properly presented, it is VC1 that this error originates from as denoted by the Receiver (<X.VC1>). I'm stumped. All of the errors I have found online show other solutions that have not worked for me. If you don't believe me when I say I know the spelling is right, A) It properly loaded the view controller, B) here is a screenshot anyway.
I DUN GET IT :[
Let me know if you have advice. Thanks all.
** AGAIN, JUST AS A REMINDER, THIS APP DOES PROPERLY LOAD AND DISPLAY VC3 AS IT SHOULD. THE ERROR OCCURS AFTERWARDS WHEN I ATTEMPT TO SEGUE TO VC4 BUT THAT SEGUE IS NORMAL THROUGH INTERFACE BUILDER WITH NO IDENTIFIER BECAUSE IT WILL NEED NO LINK TO THE CODE AS THE SEGUE TO VC3 DOES.
EDIT: Here is a sample project that functions properly. I cannot distinguish a difference that sets mine apart.
EDIT #2: I admit to making a dumb mistake. The marked solution has found it and corrected it and the project now functions as desired. Thank you all.
I study your project and found that you assign wrong class to your VC4 as shown in below image:
Just remove that HomeVC and assign new class to it and it will work fine.
It seems like that you have not set segue identifier. performSegueWithIdentifier method trying to called welcomeSegue. but its don't available so crash happen.
To removed the following crash, followed the steps.
Step 1 : Tap on segue between two UIViewController. like as
Step 2 : Now check property of that segue at right side of panel. where write identifier name as welcomeSegue.. like as
Now run your project, you will face any crash..
I have a category on UIViewController that deals with errors coming from my networking layer. If I get an authentication error in response to a network call, I want to perform an unwind segue which takes me back to my LoginViewController.
However, I don't want to have to add the appropriate unwind segue to every single view controller in my Storyboard. Can I simply declare the unwind segue in the UITabBarController that is at the "top" of my view controller navigation, and then say
[self.tabBarController performSegueWithIdentifier:#"UnwindToLoginSegueIdentifier" sender:self]
... from inside my UIViewController+ErrorHandling category?
No, you can't do that. The unwind segue has to come from the controller you're unwinding from, and all segues need to be connected from a particular instance in the storyboard. The login view controller really should be one that's presented modally, not be one of the tabs, since you only need it briefly, then it should go away. If you set up your app that way, you can present and dismiss it from any controller (present it without animation from the controller in the first tab, if that's what you want the user to see when the app launches). You'll still have to have code in every controller to do that, unless you make a common base controller with that functionality that all the other controllers inherit from.
FIRST SCENARIO:
I have two view controllers
VC1 has a button, and a label
VC2 has a button, and a text field
theres a modal segue between VC1 -> VC2
when I run this segue, we set VC1, as the delegate for VC2.
We go to VC2, fill out the text field, hit the button, and VC2 is dismissed.
some delegated method is run on VC1, and VC1.label is filled in.
question: is there any way to do this without dismissing VC2.. for example, if VC2.button just modal segues us back, or slides us back to VC1 im assuming it re initializes the viewcontroller and the label wont be changed. do you always have to dismiss the view controller
SCENARIO 2:
again, two view controllers.
this time its reversed.. so i have
VC1 with a textfield and a button
VC2 with a label and a button
soo now we fill out VC1, and we expect it to show up on VC2. But without a segue, they have no relationship. is there any way to pass data between VCs using delegation without one initial segue? Is this segue requirement to use delegation something specific to view controllers? Im assuming it is because in other cases we just instantiate objects, and use their delegate methods. but with view controllers we want to reference one that is already created, and not instantiate a second one.
note: im using story boards
1) You could do it without dismissing VC2, but it's not a good idea. You don't want to segue "back" to one, because, as you surmised, you're actually creating a new instance of VC1, and then if you segue again to VC2, you're creating a new instance of that too. You will keep piling up more and more instance of the two controllers and none will ever be deallocated.
2) Again, your instincts are correct -- you need to somehow get a reference to the instance of VC2 that your putting on screen in order to set yourself as delegate. You don't have to have a segue to do that, you could create the second controller in code and do a manual push or presentViewController, but that's functionally, the same as doing a segue.
Sorry, dont fully understand what you want .. but here is my take.
FIRST SCENARIO:
Why would you need to update the view that isnt on screen ?
Just update in viewWillAppear.
Otherwise you can have the delegate update it when you finish editing that textfield.
SCENARIO 2:
You need a link between the view controllers, use segues makes easy, set as delegate and pass along the info. Why make it harder than it needs to be
Many things have delegates, textfields etc, you are just saying this class / obj will do something for something else.
There are many youTubes about delegates, ie
http://www.youtube.com/watch?v=eNmZEXNQheE
For more info see this stack post - it covers everything you need to know
Passing Data between View Controllers