I have an app which have 4 different forms. these forms can be completed by clicking the different questions and being lead to a viewcontroller which holds the options. lets call this the OptionViewController. Now I have 4 different forms with different options but all using OptionViewController to pull data from the database, I need to unwind the segue and pass data.
Since there might be 4 different view controllers it might be coming from, I need to make sure that the information is passed properly, i.e. identify if the destinationviewcontroller the unwindsegue is performing the first, second, third or fourth viewcontroller.
I thought I might do something like this
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
if segue.identifier == "optionSelected" {
if segue.destinationViewController == FirstViewController {
//pass data
} else if segue.destinationViewController == SecondViewController {
//pass data
}
}
}
But obviously I cannot perform segue.destinationViewController == FirstViewController
What should I actually be doing? Or should i just create one OptionViewController for every form, which would solve my problem, but I am not sure if overall the app performance will drop due to the increase of view controllers
Thanks for any help in advance
To test if the destination view controller is of a specific class, use the Swift keyword is:
if segue.destinationViewController is FirstViewController {
Alternatively, you can assign the viewController to a variable using optional binding with an optional cast:
if let dvc = segue.destinationViewController as? FirstViewController {
// dvc will have type FirstViewController so you can access specific
// properties of FirstViewController using dvc
}
Related
I have login few screens and controllers in my app. First screen is screen with button and moves user to next login view with username, password field and login button. On the controller i have function onClickButton and when i have good data i request to the server with this data.
When server give me callback i have many params about user to set in label in next view.
My structure is like this
Login View -> SecondLogin View and LoginViewController -> TabBarController -> NavigationController -> Table View with TableViewController
My code is
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "afterLoginView" {
if let secondVC = segue.destination as? TabBarViewController {
secondVC.finalName = self.username
}
}
}
When i want transfer my data directly to tableViewController i have error
Thread 1: signal SIGABRT
I do not understand what I'm doing wrong
You'll need these values in almost all view controllers. Create a singleton class to store the logged in user values like this
class UserDetails: NSObject, Codable {
static let shared = UserDetails()
private override init() {
super.init()
}
var finalName: String?
var otherDetails: String?
}
Now when you receive the response from the login api, assign the values in this singleton class.
UserDetails.shared.finalName = "something"//Name received from server callback
Now you can access these values from any view controller.
class TableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
print(UserDetails.shared.finalName)
}
}
You have some work to do to get to the right view controller. Since your segue is only pointing at the UITabBarViewController, you should put in another guard or if/let statement to get you to the UINavigationController, and then another to finally get you to the UITableViewController, where you can actually refer to your finalName variable.
That would look something like:
if let secondVC = segue.destination as? TabBarViewController {
if let navCon = secondVC.viewController[0] as? UINavigationController {
if let tableVC = navCon.topViewController as? nameOfYourTableVC {
tableVC.finalName = self.username
}
The code is untested, just typed off the top of my head, so please proceed with due caution. Issues such as which tab is the correct NavController would also need to be addressed.
You need to use the actual name of your tableView class in that last if/let. A generic UITableViewController will not include your custom variables.
When server give me callback i have many params about user to set in label in next view.
This is a great example of why you should keep the M in MVC. When you get a response back from the server, store the returned data in your data model. (If you don't have a data model, you should make one.) When a view controller gets some data from the user, such as a user name, it should store that in the model. There's little reason to pass raw data back and forth between view controllers directly... just make sure that all your view controllers have a reference to the model, and have them get and set values there as needed.
This kind of approach will make your code a lot more flexible. It allows view controllers to worry about what they need to do their job, and it gets them out of the business of caring what other view controllers need.
My structure is like this
Login View -> SecondLogin View and LoginViewController -> TabBarController -> NavigationController -> Table View with TableViewController
It might make more sense to load the tab bar controller and then present the login view controller(s) modally. The view controllers that are managed by the tab bar controller can all be set up to refuse to do anything useful until the data they need is present in the data model, and that lets the tab bar controller be the root view controller. That will make it easy to set the model for each of it's child view controllers when the app starts up, and the app can then present the modal login view controllers, also set up with references to the model.
When we move the a segue from A view to B view or from b view to A view, viewdidload get data from server all the time
So, can we keep the data that viewdidload get at first excute, without starting viewdidload each page?
It's waste of time to get data all the time, when we open each page.
From now we using pageviewcontroller, I think it is inappropriate it.
I using swift language.
If you have good idea, please let me know.
There are very many tutorials on the internet that show how to do what you want. Try a google search on swift pass data between view controllers.
At its very simplest, the second controller needs to have one or properties that can hold data from the first controller. The first controller must implement the following function:
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
let secondVC = segue.destinationViewController as! ViewControllerClass
// Pass data to the second view controller.
secondVC.dataFromServer = dataFromServer
secondVC.otherData = otherData // etc.
}
The data will then be passed from the first controller to the second.
This question already has answers here:
Passing data between view controllers
(45 answers)
Closed 7 years ago.
I know that you can pass information between two view controllers if they are connected by a segue using
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
guard let destinationViewController = segue.destinationViewController as? searchTermViewController else { return }
destinationViewController.courseCodes = selectedCourses
}
}
The above code gives an error if there is no segue because of the .destinationViewController. How do i pass information between to arbitrary view controllers without having to set up a global variable?
You can set up a delegate pattern in order to do this.
Here are the steps for setting up the delegate pattern between two objects, where object A is the delegate for object B, and object B will send messages back to A. The steps are:
Define a delegate protocol for object B.
Give object B an optional delegate variable. This variable should be weak.
Make object B send messages to its delegate when something interesting happens, such as when it needs a piece of information. You write delegate?.methodName(self, . . .)
Make object A conform to the delegate protocol. It should put the name of the protocol in its class line and implement the methods from the protocol.
Tell object B that object A is now its delegate.
Here is a tutorial to give you a working example https://www.youtube.com/watch?v=9LHDsSWc680
Go to your storyboard, select the second view controller, go to the Identity inspector tab and give a StoryBoard ID value. This should be a unique value to identify your view controller.
Now in your first view controller', you can run this code. This will basically create an object of the second view controller, set the property value (for transferring data) and push it (same as the segue does)
let ctrl = self.storyboard?.instantiateViewControllerWithIdentifier("detailsView")
as? SecondViewController
ctrl?.userId = 250 // data to pass.
self.navigationController?.pushViewController(ctrl!, animated: true)
provided userId is a variable in your SecondViewController class. Replace
detailsView with the storyboard id value you gave earlier.
class SecondViewController: UIViewController {
var userId : Int = 0
override func viewDidLoad() {
super.viewDidLoad()
// do something with self.userId
}
}
I'm new to swift and am trying to write an app with it.
I have a UIViewController that I am transitioning to. I have designed the UI in interface builder and I intend to use segues to manage the transition. However, the view controller relies on data that is passed into the view controller from the previous view controller.
If I have properties on my view controller then I will need to redefine my init method. But I wouldn't normally call the init method; it would be called for me before prepareForSegue. So I see a few possible solutions:
Make my variables optional (so I can pass them in prepareForSegue
and update the view then).
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let viewController: EventViewController = segue.destinationViewController as! EventViewController
viewController.event = self.event
}
Manually init my view controller and present it programmatically instead.
???
Is there a third option? If not, which of the previously mentioned 2 is better practice?
There is two possible options as you mentioned:
The first one is the easiest which is to pass the data in prepareForSegue. which you don't have to care about dismissing the controller or keeping a track of inner view controllers,because storyboard will take care of it.
The second way is to set a Storyboard ID in storyboard,for the controller you need to present programmatically, which need more things to handle, like to dismiss the controller or keep track of inner presented controllers.
let nextViewControllerName = storyboard?.instantiateViewControllerWithIdentifier("Storyboard ID") as! nextViewControllerName
nextViewControllerName.event = self.event
self.presentViewController(nextViewControllerName, animated: true, completion: nil).
At the end they does the same purpose.
Note: You should always pass the data before presenting the controller.
I would like to use a custom initializer (with passed parameters, for dependency injection) for a view controller that is initialized in prepareForSegue. I don't understand exactly how the view controller is initialized in prepareForSegue, so not sure the correct pattern for this.
Here is the prepareForSegue code in my view controller:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "FilterPopover" {
let vc = segue.destinationViewController as! FilterViewController
vc.popoverPresentationController!.delegate = self
}
}
I would like to pass data into the FilterViewController when it is created, so that I can make the property a constant (let, not var), and do not have to use an implicit unwrapped optional. The view controller that has the above method has the data to pass into the FilterViewController custom init.
Is there a pattern for using a custom init for segue.destinationViewController so that I can pass parameters?
By the time prepareForSegue is called the destination view controller is already initialized. This is done for you by the Storyboard system which will eventually call initWithCoder: on your view controller. You could initialize your let properties here.
If you want to use a custom initializer you would have to create the controller in code without using storyboards in that situation.
To complete that Joris said, in that case, you can additionnaly use a .xib separated file for your ViewController