Swift passing data between views - ios

I am working on a Swift app and I've run into a slightly weird issue. I'm quite new to Swift and iOS development so think I may not be doing this the correct way.
I have a UITableView and I am able to read the contents of the selected cell without issue:
override func tableView(_ tableView: UITableView, didSelectRowAt
indexPath: IndexPath) {
let cell = self.tableView.cellForRow(at: indexPath)
selectedItem = (cell?.textLabel?.text)!
}
I have a variable set at the top of my class like so:
var selectedItem: String = String()
I then use a prepare for seque to seque to the new page/view and pass in the data selected:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showDetails"
{
if let destinationVC = segue.destination as? ItemDetails {
destinationVC.detailsToDisplay = selectedItem
}
}
}
The problem is that while the data does get passed between the views and appears in the UI, it's not the most recent data. For example when I run the code and select the item, the first time, nothing appears, but if I click the back button, select the item again, this time it does appear in the next view. And on subsequent selections in the UI, it navigates to the next view but doesn't update the text until I pick another item.
In my mind it's almost like the selectedItem variable is not being set on each selection as I expect and passed through, but instead on subsequent selections in the UI, it is updated.
How do I get this to pass the actual correct item each time and not the previous one or none at all (which is what happens on the first run).
Thanks!

Your prepare for segue called before the didSelectRow at index
change the code like below
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showDetails"
{
if let destinationVC = segue.destination as? ItemDetails {
let cell = tableView.cellForRowAtIndexPath(self.tableView.indexPathForSelectedRow!) as UITableViewCell
destinationVC.detailsToDisplay = (cell.textlabel?.text)!
}
}
}

If you want to pass data between controllers, you can create a global variable declared above the class. Store the result in that global variable.
For example,
In ViewController class:
var result:String!
class ViewController:UIViewController{
result = // your data }
In SecondViewController class:
class SecondViewController:UIViewController{
var fetch = result
if(fetch != result){
print("Got the value")
}
else
{
print("Null value")
}

Related

Passing a string from a TableView cell into a View Controller in Swift 3

In Swift 3, I'd like to pass the string contained in a cell of a UITableView into the subsequent View Controller.
As a note, I read several answers to this that seem to be specific to earlier versions of Swift, so I wanted to reopen. As an example, the last solution here looked straightforward, but I can't get it to work.
In TableViewController, I create a string of an audio file's name and date posted with this JSON extraction:
if let shows_list = json as? NSArray
{
for i in 0 ..< data_list.count
{
if let shows_obj = shows_list[i] as? NSDictionary
{
let episode_name = shows_obj["episode"] as? String
let episode_date = shows_obj["date"] as? String
TableData.append(episode_date! + " | " + episode_name!)
let testString = TableData.append(episode_date! + " | " + episode_name!)
// the part above runs and will add the string into each cell. now here, I want to pass the value of that string into a UILabel called episodeTitle in the target view controller
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let indexPath = self.tableView.indexPathForSelectedRow {
let destinationVC = segue.cellPasser
EpisodeViewController.text = testString //this is the string in second view controller
}
}
}
}
}
This is throwing two errors:
Value of type 'UIStoryboardSegue' has no member 'cellPasser'
In the Storyboard, I have added an identifier to the Show segue called 'cellPasser.' My belief is I should be able to call that here but it's being flagged.
EpisodeViewController.episodeTitle = testString
I have an outlet in EpisodeViewController that is called episodeTitle, so I am wondering if there is a preferred way other than this to pass strings into an element like that.
I'm new to Swift -- hoping someone sees a better way to do this.
Edit: In case useful, here's the View Controller linked from the segue.
class EpisodeViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBOutlet var episodeTitle: UILabel!
Here are steps to pass value from TableViewController to next ViewController :
in TableViewController You should declare a didSelectRowAt method.
Declare a method prepare for segue
Do not forget to declare a variable in second view Controller.
1.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.performSegue(withIdentifier: "Identifier", sender: indexPath)
}
2.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "Identifier" {
let vc = segue.destination as! NameOfYourViewController
vc.variableInSecondVc = variableInTableView
}
}
3.
var variableInSecondVc = ""

Passing value from Firebase from one tableView to another

I am trying to pass value I get from Firebase to another tableView. I get 2 values from Firebase - "Brands" and "Products". I am trying to make like car app. If you click on Ford then new tableView will appear and shows all the Ford models. This is what I've done so far.
like this I get Brands from Firebase:
func parseSnusBrands(){
let ref = FIRDatabase.database().reference().child("Snuses").child("Brands")
ref.observeSingleEventOfType(.Value, withBlock: { (snapshot) in
if snapshot.exists() {
if let all = (snapshot.value?.allKeys)! as? [String]{
for a in all{
if let products = snapshot.value![a] as? [[String:String]]{
self.snusBrandsArray.append(["key":a,"value":products])
}
}
self.snusBrandsTableView.reloadData()
}
}
})
}
And like this I detect which cell is clicked and print the product that belongs to the clicked Brand:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath){
print("products at \(indexPath.row) --> \(snusBrandsArray[indexPath.row]["value"])")
}
How to pass the (snusBrandsArray[indexPath.row]["value"]) to new tableView? I tried using segues and looking for tutorials like "How to pas value between viewControllers" but I am out of luck. Right now I have 2 tableViewController.swift files and one tableViewCustomCell.swift file. Do I need some more files?
For send data, first of all declare your variable in 2nd view controller..
var productsValue = [[String:String]]()
and in 1st viewcontroller
var valueTopass = [[String:String]]()
Than in didSelectRowAtIndexPath, take a value in one valueTopass
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("products at \(indexPath.row) --> \(snusBrandsArray[indexPath.row]["value"])")
if let products = snusBrandsArray[indexPath.row]["value"] as? [[String:String]]{
valueTopass = products
performSegueWithIdentifier("toProducts", sender: self)
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?){
if (segue.identifier == "toProducts") {
var viewController = segue.destinationViewController as! SnusProductsTableViewController
viewController.productsValue = valueTopass
print(productValues)
}
}
You need to use Segues to pass the data forward.
To pass data from the current view controller to the next new view controller using segues, first create a segue with an identifier in the relevant storyboard. Override your current view controller's prepareForSegue method. Inside the method check for the segue you just created by its identifier. Cast the destination view controller and pass data to it by setting properties on the downcast view controller.
Setting an identifier for a segue:
Segues can be performed programatically or using button action event set in the storyboard by ctrl+drag to destination view controller.
You can call for a segue programatically, when needed, using segue identifier in the view controller:
func showDetail() {
performSegueWithIdentifier("showDetailingSegue", sender: self)
}
You can configure segue payload in the override version of prepareForSegue method. You can set required properties before destination view controller is loaded.
Swift
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showDetailingSegue" {
let controller = segue.destinationViewController as! DetailViewController
controller.isDetailingEnabled = true
}
}
DetailViewController is the name of the second view controller and isDetailingEnabled is a public variable in that view controller.
To expand on this pattern, you can treat a public method on DetailViewController as a pseudo initializer, to help initialize any required variables. This will self document variables that need to be set on DetailViewController without having to read through it's source code. It's also a handy place to put defaults.
Swift
func initVC(isDetailingEnabled: Bool) {
self.isDetailingEnabled = isDetailingEnabled
}
Why not pass the whole dictionary with all the contents from firebase to the new VC using prepare for segue?
And then store the dict in the destinationVC data model?
That should do the trick.

Passing Core Data objects to another view controller when selecting row from UITableView

EDIT based on provided answer:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
selectedName = quizNames[indexPath.row]
performSegueWithIdentifier("saveSegue", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "saveSegue" {
let inputController = segue.destinationViewController as? QAInputViewController
inputController?.quizName = selectedName
}
}
I have done some research on here trying to figure out exactly how to use prepareForSegue in order to pass the selected Core Data object from a UITableView cell to a second View Controller, but what i found and tried to implement did not work out for me as it seems some of the code might be outdated. Here is what i am trying to do:
I populate a UITableView with an array of names of type Name which is my NSManagedObject. When i select a specific name it will segue me to an input screen where a question and answer will be submitted. I want to make sure that the question and answer that are submitted stay assigned to the previously selected name. I am not sure how to set this up in prepareForSegue. I did set up my data model with relationships, so if i did that correctly it should work once i understand how to implement it. Is there some way i can check the currently selected name so that i know any question and answer that is inputted will be saved to that name?
Relative to my above question, i want the title of the second View Controller to show the name of the previously selected name.
Basically i am just looking for the guidance on to get this implemented in Swift 2. Thank you very much for your help.
EDIT: Passing Core Data objects from UITableViewCell to another View Controller
The above link is the solution i tried implementing that was not working for me. Specifically:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showTaskDetail" {
let detailVC: TaskDetailViewController = segue.destinationViewController as! TaskDetailViewController
let indexPath = self.tableView.indexPathForSelectedRow()
let thisTask = fetchedResultsController.objectAtIndexPath(indexPath!) as! TaskModel
detailVC.detailTaskModel = thisTask
detailVC.delegate = self
}
This is how I do it:
At the top of my class I'll have a var that stores the selected item:
var selectedTask: Task?
Then in my tableViews didSelectRow method:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
selectedTask = tasks[indexPath.row]
performSegueWithIdentifier("showTaskDetail", sender: self)
}
And finally:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showTaskDetail" {
let detailVC: TaskDetailViewController = segue.destinationViewController as! TaskDetailViewController
detailVC.detailTaskModel = selectedTask
detailVC.delegate = self
}
}
However, I'd be more inclined to just pass some kind of ID/name and perform another fetch with a new moc rather than pass around core data objects.
You'll get less threading issues this way and it will make things far easier should you need to deep link into your app, from say, a notification or something.
Mocs are cheap, use them once, and throw them away.

Send variable value to next view controller when click a table cell

I have two table view controllers
InvoiceList view controller
InvoiceShow view controller
I use didSelectRowAtIndexPath method as bellow to get selected table cell specific value
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let rowObject = objects[indexPath.row]
let invoicehash = rowObject["hash_key"]!
}
i need to send invoicehash value to InvoiceShow controller when click the table cell of InvoiceList
i tried to use prepareForSegue function. but it is not applicable because it will trigger before the didSelectRawAtIndexPath function. so when i implemented it, gives the previous click event variable value. not correct one.
Please help me to access invoiceHash variable value from InvoiceShow controller
You will get the selected cell in prepareForSegue method itself.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let selectedIndexPath = self.tableView.indexPathForSelectedRow()!
let rowObject = objects[selectedIndexPath.row]
let invoiceHash = rowObject["hash_key"]!
let invoiceShowViewController = segue.destinationViewController as! InvoiceShowViewController
// Set invoiceHash to `InvoiceShowViewController ` here
invoiceShowViewController.invoiceHash = invoiceHash
}
You can still use a segue if you want and/or already setup on your storyboard.
You just need to connect the two view controllers in Interface Builder directly from one to another.
So, start ctrl-dragging from the controller itself and not from the TableViewCell (take a look at the screenshot)
then use the performSegueMethod with the new segue identifier like this:
self.performSegueWithIdentifier("mySegueIdentifier", sender: self)
and finally, your prepareForSegue method:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "mySegueIdentifier" {
let selectedIndex = self.invoiceTableView.indexPathForSelectedRow
//if element exist
if selectedIndex?.row < myDataSourceArray.count {
let destination = segue.destinationViewController as! InvoiceShowViewController
let invoice = myDataSourceArray[selectedIndex!.row]
destination.invoice = invoice
}
}
}
That's it!

Segue pushing Nothing first time(delay in data sent)

Hello StackOverflow,
I'm just picking up swift and trying to implement data being passed between UITableView Cell to a UIViewController which will show a detailed view of the info shown on the tableview, and whenever I test the application on my emulator first time I press a table cell it passes an empty string and then when I try pressing another cell the viewController shows the string that was supposed to be seen earlier.I pasted the code I have for my tableview didSelectRowAtIndexPath below.
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: false)
var7 = lat[indexPath.item]
var6 = long[indexPath.item]
var5 = items[indexPath.item]
var1 = detail[indexPath.item]
var2 = date[indexPath.item]
var3 = wop[indexPath.item]
var4 = ViewController()
nextView.locationPassed = var1
//self.performSegueWithIdentifier("DetailPush", sender: self)
println("value stored in var1: \(var1)")
//println("The selected indexPath is \(indexPath.item + 1)")
println("The stored id is: \(storeSend)")
}
Here is my implementation for my push segue method
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "DetailPush"
{
if let crimesView = segue.destinationViewController as? ViewController {
crimesView.locationPassed = var1
//println("The passing address is: \(var1)")
}
}
}
Any idea on why I'm getting data delayed during the segue?
Thank you
Solution Found: I edited my prepareForSegue method with the following and it fixed my issue
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
//Adding the indexPath variable for the selected table Row within the segue
var indexPath: NSIndexPath = self.tableView.indexPathForSelectedRow()!
if segue.identifier == "DetailPush"
{
if let crimesView = segue.destinationViewController as? ViewController {
//Then just pass the data corresponding to the array I created identified by the index of the selected row
crimesView.locationPassed = self.arrayName[indexPath.row]
println("The passing address is: \(self.addressSend)")
}
}
}
I found the solution by watching some online videos and all I did to fix my issue was redefine my prepareForSegue function with:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
//Adding the indexPath variable for the selected table Row within the segue
var indexPath: NSIndexPath = self.tableView.indexPathForSelectedRow()!
if segue.identifier == "DetailPush"
{
if let crimesView = segue.destinationViewController as? ViewController {
//Then just pass the data corresponding to the array I created identified by the index of the selected row
crimesView.locationPassed = self.arrayName[indexPath.row]
println("The passing address is: \(self.addressSend)")
}
}
}
And it seems to work like a regular segue for me.......Thank you for all the suggestions given me
you said you are doing the prepareForSegue from async request
so try this:
if segue.identifier == "DetailPush"
{
dispatch_async(dispatch_get_main_queue()) {
if let crimesView = segue.destinationViewController as? ViewController {
crimesView.locationPassed = var1
//println("The passing address is: \(var1)")
}
}
}
try to remove the line
tableView.deselectRowAtIndexPath(indexPath, animated: false)
see if it still happens.
maybe move it to the end

Resources