Passing same variable to container view with different value - ios

I have one button on main viewController which has default value nil which is associated by dropDown pod.
On same viewController there is also a container view.
During first time loading, I get the default value of a variable from shared preferences and pass that value to container view by performSegue.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if(segue.identifier == "dataToContainerView"){
DispatchQueue.main.async {
var secondVC = segue.destination as! secondViewController //container viewController
secondVC.variable = self.variable
}
}
}
Now I need to pass the value of same variable again by selecting from dropdown button by user.
dropDown.selectionAction = { [unowned self] (index, item) in
self.button.setTitle(item, for: UIControlState())
self.variable = item
print(item)
self.performSegue(withIdentifier: "dataToContainerView", sender: nil)
//performing segue to resend the new value of the variable.
}
The above code performs properly till print(item).
But I am getting the following error on performSegue.
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'There are unexpected subviews in the container view. Perhaps the embed segue has already fired once or a subview was added programmatically?
How should I pass the value to container view second time overriding first value with the help of dropDown pod?
update:- I need the variable value so that I can pass it to json parser on container viewController. And the code on container viewController re-executes.

You need to save a reference to your embedded controller so that you can update it again later. Do this in the first segue:
// Declare a local variable in your parent container:
var secondVC: secondViewController!
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if(segue.identifier == "dataToContainerView"){
DispatchQueue.main.async {
self.secondVC = segue.destination as! secondViewController
//container viewController
self.secondVC.variable = self.variable
}
}
}
Then later when you need to update the variable you can just reference it directly:
self.secondVC.variable = self.variable
self.secondVC.viewDidLoad()

Related

Segue to chat viewcontroller

I have included images to hopefully make this easier to understand. My FirstViewController has a collection view with a list of users from my firebase database.When I click on the users I am segued to a DetailedViewController that has more information about the user that was clicked. Within that viewController, the goal is to click on the compose button and segue to a viewController that allows me to chat with the user, whose profile I clicked on.
I have gotten as far as this for segueing from DetailedViewController to Message user 1.
#IBAction func SendMessage(_ sender: Any) {
performSegue(withIdentifier: "chat", sender: self)
}
I am not sure how to make sure I am sending the particular user I click on a message.
This is how I am passing data from FirstViewController to DetailedViewController.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "details" {
if let indexPaths = self.CollectionView!.indexPathsForSelectedItems{
let vc = segue.destination as! BookDetailsViewController
let cell = sender as! UICollectionViewCell
let indexPath = self.CollectionView!.indexPath(for: cell)
let post = self.posts[(indexPath?.row)!] as! [String: AnyObject]
let Booked = post["title"] as? String
vc.Booked = Booked
print(indexPath?.row)
} }
One route to take is in your DetailViewController class, or whatever class you have implementing performSegue(withIdentifier:, sender:), add this:
override public func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard segue.identifier == "chat", let dest = segue.destination as? ChatViewController else {
return
}
dest.user = self.user
}
I don't know how you have the user declared but if user is an object, do what I have above. If you're saying user as a way to umbrella multiple properties you want to pass.. do this:
dest.name = self.name
dest.number = self.number
//etc
prepare(for segue:, sender:) allows you to intercept any segue from it's class and set up whatever variables needed before the performSegue(...) executes. To target your code to a specific segue/destination/situation, make sure your code runs a check on those constraints before executing; otherwise the code will execute on all segues implemented in the class. In my example, I used your segue's identifier as that check.

How do I get the property value from a container view in Swift

I have 1 UIViewController that contains a UIContainerView and a UIButton.
Also, I have a UITableViewController (It has a UITextField and a UITextView) that is embedded in the UIContainerView in the UIViewController.
I'm trying to get the string values that will be available in TextField and in TextView.
I tried to use the segue to get the properties values, but I failed to do so, see the code below.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "TVCSegue" {
let containerTVC = segue.destination as? FieldsTVC
self.textField.text = FieldsTVC?.textField?.text
self.textView.text = FieldsTVC?.textView?.text
}
}
The code above has 'textField' and 'textView' as properties in the UIViewController to assign the values to.
But I believe that it doesn't work as I get the values before they changes. Please provide me with a functional way to do so.
When your main view loads, the container performs a segue to its assigned initial / root ViewController. At that point, you can get a reference to it:
var theFieldsTVC: FieldsTVC?
Now, in prepare for segue, assign that variable:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "TVCSegue" {
if let vc = segue.destination as? FieldsTVC {
theFieldsTVC = vc
}
}
}
Then, you can:
#IBAction func buttonTapped(_ sender: Any) {
self.textField.text = theFieldsTVC?.textField?.text
self.textView.text = theFieldsTVC?.textView?.text
}

prepareForSegue and PerformSegueWithIdentifier sender

I am wondering about how the functions in the title work and also about the sender parameter.
Lets say a button click calls the performSegue method, does that also call the prepareSegue method as well? Is the prepareSegue method called before the performSegue method but after the button is pressed?
Also, is the "sender" parameter in both of the functions linked? If I pass in a string as the sender in the performSegue method, will that transfer over to the sender parameter in the prepareSegue method? In other words, if I set the sender parameter in the performSegue method as "Hi world", will the sender parameter in the prepareSegue method also be the same string?
Thanks
There are, effectively, two ways you can trigger a segue. The first is via an action on a UI element in Interface Builder, the second is using performSegueWithIdentifier:sender: in your code. I say 'effectively', because under the covers, when the scene is loaded from the storyboard, an action handler is configured that ultimately calls performSegueWithIdentifier:sender:
When performSegueWithIdentifier:sender: is called, the segue object is delivered to your view controller's prepareForSegue:sender: function.
In the case where the segue was initiated by an action on a UI element then the sender will be that UI element (i.e. if it is an action connection on a UIButton then the sender will be the UIButton instance).
If the segue is initiated by your code calling performSegueWithIdentifier:sender: then the sender will be whatever object you passed as the sender. This could be your view controller, a button, an array, anything. So yes, if you pass "Hello World" to performSegueWithIdentifier:sender: as the sender value then this will be the sender in prepareForSegue:sender:
In terms of the order of operations:
performSegueWithIdentifier:sender is called, either by your code or as a result of an action on a UI element
If your view controller implements shouldPerformSegueWithIdentifier:sender: then this function is called. If this function returns false then the segue is cancelled
The segue object and destination view controller object are created
If your view controller implements prepareForSegue:sender: then this function is called.
Once prepareForSegue:sender: returns, the segue completes.
The performSegue method calls a segue to be performed from one view to another. Before the segue actually takes place, the prepareForSegue method is called, and if you want to pass data between the views, you'd do it there.
The performSegue method doesn't take the parameter you want to send. It's only used to call the segue in the first place. Any data that you want to send will be done through prepareForSegue.
Here's an example.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
performSegueWithIdentifier("test", sender: self)
//You can set the identifier in the storyboard, by clicking on the segue
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "test"{
var vc = segue.destinationViewController as! RandomViewController
vc.data = "Data you want to pass"
//Data has to be a variable name in your RandomViewController
}
}
Let me know if this helps!
The_Curry_Man's answer worked for me. Here's an update of his code for Swift 3.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
performSegue(withIdentifier: "test", sender: self)
//You can set the identifier in the storyboard, by clicking on the segue
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "test"{
var vc = segue.destinationViewController as! RandomViewController
vc.data = "Data you want to pass"
//Data has to be a variable name in your RandomViewController
}
}
my two cents for beginners... In swift 3 is:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
}
So, if arriving controller (of class MyController) implements a "fillData" method:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destController = segue.destination as MyController{
destController.fillData(...)
}
}
Updated Method for Swift 5
performSegue(withIdentifier: "showNextViewController", sender: self)
Note : "showNextViewController" is identifier added for segue in storyboard
while sending any object to the particular object to another view controller by using perform segue with an identifier, Please follow the steps #Swift4
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "Detailed_Live_poll"{
let destinationNavigationController = segue.destination as! UINavigationController
let targetController = destinationNavigationController.topViewController as! NewLivePollViewController
targetController.dictQuestInf = sender as! NSDictionary
}
}

How can i create a segue that pass data to another view controller only if certain criteria are met

I want to create a segue to pass data to another view controller but there are certain criteria that must happen for the segue to happen. If possible i would prefer to use the segue Id instead of the dragging method.
this is an example Im trying to accomplish
#IBAction func SubmitButtonPressed(sender: AnyObject) {
if 1<0 {
// dont perform segue
}else{
//Perform segue
// i want to pass this data in the next VC
var data = "foo"
//this is my segue id i want o use to go to the Second VC
var segueId = "segueForgotPasswordTwo"
// second VC
var secondVc = "viewController2"
// Iwant to to use prepare for segue but im getting errors in the parameters
prepareForSegue(UIStoryboardSegue, sender: AnyObject?){
}
}
}
Your question is a bit unclear but I believe this is what you are looking for...
func someFunction(){
if //some condition {
//code
}else if //some condition {
//code
} else {
//perform segue by using the folowing line. Assign the identifier to the segue in the storyboard.
//Do this by first creating a segue by dragging from a view controller to the destination view controller. Be sure to drag from the VIEWCONTROLLER, to the destination VIEWCONTROLLER. DO NOT just drag from the button. Next, choose the type of segue (eg. show or present modally), and then type in an indentifier for this segue.
performSegueWithIdentifier("SegueIdentifier", sender: nil)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "SegueIdentifier" {
//now find your view controller that you are seguing to.
let controller = segue.destinationViewController as! SomeViewController
//access the properties of your viewController and set them as desired. this is how you will pass data along
controller.property = someValue
}
}
Overview:
Hook the segue from the source view controller to the destination view controller (see left side red arrows)
Don’t hook it from the button to the destination view controller
Create an action for the button to do your custom condition check then perform segue
Screenshot:
Code:
var data = "foo"
#IBAction func buttonPressed(sender: UIButton) {
let someCondition = true
if someCondition {
performSegueWithIdentifier("showGreen", sender: self)
}
else {
performSegueWithIdentifier("showPink", sender: self)
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showGreen" {
let greenVC = segue.destinationViewController as! GreenViewController
// Make sure the data variable exists in GreenViewController
greenVC.data = data
}
}
You can implement the shouldPerformSegueWithIdentifier function in your ViewController. When the segue is triggered, this function can cancel the segue if it returns false, so you can simply include whatever logic is required in this function and return true/false as appropriate.

class data becomes nill when I reference it in my 'prepareForSegue' method

I have a tableview with buttons each containing a tag (according to indexPath.row), and when I press a particular button the details of the contents of that cell (which relates to a class called EventEntity) is meant to be passed through a 'prepareForSegue' method to another screen.
The value is assigned in the 'infoButtonDidPress' method as shown below:
func infoButtonDidPress(sender: UIButton){
eventPressed = eventEntities[sender.tag]
println (eventPressed.name) // prints the name, all good here.
}
The problem is that when I assign the contents of eventPressed to the destinationVC in 'prepareForSegue', they are nil. Code below:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if (segue.identifier == "infoButtonSegue"){
var svc = segue.destinationViewController as! EventDetailsViewController;
println (eventPressed.name) // NOPE.
svc.name = eventPressed.name //also NOPE!
}
eventPressed is declared above viewDidLoad:
var eventPressed: EventEntity! = EventEntity()
The error I get when I try either of those lines with the 'NOPE' comment is 'Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Appname.EventEntity name]: unrecognized selector sent to instance 0x7fa280e16a50' error
Whats going on here?
Thanks in advance!
UPDATE
I have moved performWithSegue to the infoButtonDidPress method, but I am now unable to reference the destinationViewController.
My infoButtonDidPress now looks like this:
func infoButtonDidPress(sender: UIButton){
let eventPressed = eventEntities[sender.tag]
var destinationViewController: UIViewController = self.destinationViewController as EventDetailsViewController
// error: the current VC does not have a member named destinationViewController
self.performSegueWithIdentifier("infoButtonSegue", sender: self)
}
As you discovered, prepareForSegue is happening before infoButtonDidPress is called.
To fix this, instead of wiring the segue from the prototype cell in your tableView, wire it from the viewController icon at the top.
See this: How to wire segue from ViewController icon
Make sure to set the identifier to "infoButtonSegue". Then call the segue from infoButtonDidPress after you have set the value for eventPressed:
func infoButtonDidPress(sender: UIButton){
eventPressed = eventEntities[sender.tag]
self.performSegueWithIdentifier("infoButtonSegue", sender: self)
}
And then, in prepareForSegue pass the value to the destination view controller:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if (segue.identifier == "infoButtonSegue") {
let dvc = segue.destinationViewController as! EventDetailsViewController
dvc.name = eventPressed.name
}
}

Resources