setting variables between view controllers - swift

I'm new to swift and I'm trying to learn how to pass data between view controllers and use firebase along with it. This is my segue to a new controller with it setting a variable on that view controller I'm pushing to.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let viewController = storyboard?.instantiateViewController(withIdentifier: "UserInfoViewController") as! UserInfoViewController
viewController.uidPassed = userUIDArray[indexPath.row]
self.present(viewController, animated: true, completion: nil)
}
It segues to the new controller where I have the variable declared as a string. When I go to run this it crashes with it saying that the string was empty. I can print it and it is empty in the viewDidAppear method, but the viewDidLoad method it has the UID stored in the variable from when I clicked on in the previous controller. Am I not passing the variable from one view controller to the other correctly?
override func viewDidAppear(_ animated: Bool) {
databaseRef.child("Users").child(self.uidPassed).child("Name").observe(.value, with: { (snapshot) in
let name = snapshot.value as! String
print(name)
})

There are two main ways to pass data from one view controller to another view controller.
First, you can connect your cell with the destination view controller in storyboard. In this case you will define the type of segue in storyboard, and you need to define an identifier for the segue in storyboard. When you want to pass data, you need to do it in prepareForSegue method, like this
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destinationViewController = segue.destination as? YourTableViewController {
destinationViewController.texts = self.texts
//...
}
}
Second, you can do this programmatically, like what you are trying to do in your example. In this case you should not connect view controllers by segue in storyboard. Instead, you set an identifier for the destination view controller itself. Then you initiate that controller in the code, set properties to the initiated view controller object and then present it, like what you are doing in your example.
Hope that helps.

This sounds like a typing issue. It seems like userUIDArray holds an array of UID's (not sure what type this is), and you're trying to assign it to viewController.uidPassed, which sounds like it's expecting a string. Try this:
viewController.uidPassed = userUIDArray[indexPath.row] as! String
The other option is to change the type of uidPassed to whatever type of value the userUIDArray variable holds.

Related

Open a ViewController if a cell of a TableViewController is clicked

i'm trying to create a view controller programmatically that will be opened if a cell on a table is clicked. i'm using a table view and i'm filling it with a xib.So, i'm stuck at the point of referencing the new view controller from the first one on click on a cell created in the table view via xib.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vc = storyboard?.instantiateViewController(withIdentifier: "DViewController") as? DViewController
self.navigationController?.present(vc!, animated: true, completion: nil) //present della view successiva
vc?.name = data[indexPath.row].nome
}
this code allows me to click on the row but when clicked it shows an error "Fatal error: Unexpectedly found nil while unwrapping an Optional value" generated apparently from the self.navigationController?.present(vc!...the vc value result to be nil and i can't figure out why.
THis is the ViewController that i want to open on click and the only thing that it has to do is open and change the tile to the name ow the cell that i've clicked onto in the other ViewController
class DViewController: UITableViewController {
var name = ""
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.navigationBar.topItem?.title = "\(name)"
}
}
Can someone help?i'm new to swift...ty
Based on what you mentioned. You can initialize your viewController without stroyboard, and there is no crash anymore.
let vc = DViewController()
If you present your view controller like what you did, it won’t display the title you wish.
To display the title, there are 2 ways you can do:
Display your new view controller with Pushing style:
let vc = DViewController()
navigationController?.pushViewController(vc, animated: true)
Keep using presenting style, but you need to wrap your view controller in other navigation controller:
let vc = DViewController()
let nav = UINavigationController(rootViewController: vc)
navigationController?.present(nav, animated: true)

Any idea as to why my data isn't being passed to the new tableview?

I am passing data from one tableview to another. I want the category data that the tableviewA contains to be passed to tableviewB. When I perform the segue, the print data that I have for TableviewB is empty.
This is tableviewA
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let category = listOfCategories[indexPath.row].strCategory
let vc = MealsByCategoryVC()
vc.mealCategory = category
print(category) // Properly returns the category
performSegue(withIdentifier: "searchMeals", sender: nil)
}
This is tableview2
class MealsByCategoryVC: UITableViewController {
var mealCategory : String = ""
var listOfMeals : [Meals] = []
override func viewDidLoad() {
super.viewDidLoad()
print("Meal category is \(mealCategory)") //This statement returns "Meal category is "
}
This:
let vc = MealsByCategoryVC()
vc.mealCategory = category
performSegue(withIdentifier: "searchMeals", sender: nil)
...is not how you pass a value into a view controller that you are creating by calling performSegue. The first two lines of that code do nothing at all! The view controller created by the segue is different from the one you are creating by saying MealsByCategoryVC(); in fact, the latter is just thrown away, uselessly. You are setting the mealCategory of the wrong view controller instance.
Instead, implement prepare(for:sender:). That's what it's for. You receive the segue and its destination view controller. That is the view controller whose mealCategory you need to set.

Xcode Cannot go back to TableViewController in navigation stack

I have controller1 -> TableViewController2 -> TableViewController3 in my storyboard. When I press a button in controller1, I want to jump to TableViewController3 and from there when I select a row, I want to go back to TableViewController2 and get some data and then go back to controller1.
In controller1 instantiate TableViewController3:
if segue.identifier == "MySegue" {
let viewController:TableViewController3 = segue.destination as! TableViewController3
viewController.addLocationToMap = true
}
In TableViewController3 instantiate TableViewController2 like this:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if addLocationToMap == true {
let navc:UINavigationController = self.navigationController!
let tvc:UITableViewController = TableViewController2()
let rootView = navc.viewControllers[0]
navc.setViewControllers([rootView, tvc], animated: true)
return
}
In TableViewController2 viewDidLoad, it fails in ViewDidLoad when I try to set a text field value because the textfield is nil. It cannot be because Textfield is already in the view. Looks like the TableViewController2 view never got loaded.
in TableViewController2
override func viewDidLoad() {
super.viewDidLoad()
locationPurposeTextField.text = "sometext"
}
Fails when setting text value because locationPurposeTextField is nil.
How to fix this?
EDITS:
On pressing a button In controller1:
let navc:UINavigationController = self.navigationController!
let alTvc = self.storyboard!.instantiateViewController(withIdentifier: "AddLocationID") as! UITableViewController
navc.pushViewController(alTvc, animated: false)
let cListTvc = self.storyboard!.instantiateViewController(withIdentifier: "ContactListID") as! UITableViewController
navc.pushViewController(cListTvc, animated: true)
The code takes me to TableViewController3 with storyboard ID: ContactListID as desired.
Next, in TableViewController3
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.myDelegate?.userSelectedContact(contactIdentifier: self.contactId, contactFullName: fullName, contactSelected: true)
let navc:UINavigationController = self.navigationController!
let tvc:UITableViewController = self.storyboard!.instantiateViewController(withIdentifier: "AddLocationID") as! UITableViewController
let rootView = navc.viewControllers[0]
navc.setViewControllers([rootView, tvc], animated: true)
}
Takes me to TableViewController2 with storyboard ID: AddLocationID
Based on the delegate sent TableViewController3, in ViewDidappear method of TableViewController2, I set some text in the view and call tableview.realoadData(). It does not load the view.
However if I select a button in TableViewController2 and load TableViewController3 and then comeback to TableViewController2 upon execution of the very same method didSelectRow it reloads the view in TableViewController2
How to fix it, please let me know?
There are a couple of things wrong with your approach.
If you want to go from controller1 to TableViewController3 but have your navigation stack contain controller1 -> TableViewController2 -> TableViewController3, you will have to have the button in controller1 first push TableViewController2 without animation, and then push TableViewController3 with animation. (You won't be able to have your button trigger a segue.)
Second problem: You can't create a copy of your TableViewController2 by just invoking it's initializer (TableViewController2()). When you do that it doesn't load its views from the storyboard. Instead, you should assign a storyboard identifier to it and use the UIStoryboard method instantiateViewController(withIdentifier:) to create a new instance of it.

Call a textfield from other view controller in swift (prepareforsegue)

I read a lot of posts and videos of this and I read that is with a function calls "prepareforsegue" but I don´t understand where to implement and what other things do I have to do. I have 4 textfields that I want to call from another later controller. Thank you very much for the answer. :)
You can't set values or access to textfields or any other outlet inside prepareforsegue method. That because prepareforsegue method is called "before" the destination controller's outlets get set. So you need to create a variable in your destination controller that receives the value for the textfield and then in viewDidLoad in destination controller set it to the textfield.
In prepareforsegue method :
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let destinationViewController = segue.destinationViewController as? SecondViewController {
destinationViewController.valueForTextField = "Hi"
}
}
In the destination controller (which is SecondViewController in my case) :
#IBOutlet weak var textField: UITextField!
var valueForTextField: String?
override func viewDidLoad() {
super.viewDidLoad()
textField.text = valueForTextField
}
It helps to post your code.
Inside prepareForSegue, grab the destination ViewController that holds your textFields by using:
let vc = segue.destinationViewController
then vc.textField1.text = "Your Text here" & repeat for your other text fields.

Swift: Segue Error when calling

I have a segue setup in my swift class called ViewController and I am calling from a tableView didSelectRowAtIndexPath. I am using this code in my segue
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
var svc = segue!.destinationViewController as Homework;
svc.subject = subject_name
}
To tell it to set a varible called subject which is declared like this var subject:NSString! to a varible called subject_name. I then call it from my tableView didSelectRowAtIndexPath using this code prepareForSegue(UIStoryboardSegue(), sender: AnyObject?()).
This is my didSelectRowAtIndex
func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
println("You selected cell #\(indexPath.row)!")
let indexPath = tableView.indexPathForSelectedRow();
var currentCell = tableView.cellForRowAtIndexPath(indexPath) as UITableViewCell
println(currentCell.textLabel!.text)
subject_name = currentCell.textLabel.text
// // Showing new storyboard
performSegueWithIdentifier("Homework", sender: self)
let vc : AnyObject! = self.storyboard.instantiateViewControllerWithIdentifier("Homework")
self.showViewController(vc as UIViewController, sender: vc)
}
But when I go and run the app, and click the table view cell I get this error "fatal error: unexpectedly found nil while unwrapping an Optional value", and a green arrow points towards var svc = segue!.destinationViewController as Homework;. I tested the exact same code out on another app expect with the segue being called when a button is clicked and that worked perfectly, I also tried answers from Calling segue programatically not working, and Preparing for segue in embedded tableView in Swift. Both of these answers did not work.
If I understood it correctly, you are calling prepareForSegue(). You shouldn't, that is automatically invoked.
What you should do instead is calling performSegueWithIdentifier(identifier: String, sender: AnyObject?). That triggers a segue invocation, which automatically executes prepareForSegue().
The identifier parameter is the one you set from IB: select the segue and look at the attributes inspector.
Besides that, #AnthonyKong's answer is a safer way to deal with optionals (a segue in this case) - that ensures that no runtime exception is thrown.
Addendum
Looking at your updated question, specifically at the implementation of didSelectRowAtIndexPath. The last 2 lines:
let vc : AnyObject! = self.storyboard.instantiateViewControllerWithIdentifier("Homework")
self.showViewController(vc as UIViewController, sender: vc)
are redundant - if you perform a segue, that will instantiate the destination view controller, so you don't have to do it manually. Remove those lines.
You should do this instead:
if let svc = segue!.destinationViewController as? Homework {
svc.subject = subject_name
}
It is because you might get passed other segues which do not have Homework as desination VC