Swift: Passing Variable between Views not updating first time - ios

I have a tableview with cell data. When I click the cell, it takes me to another view controller and displays the cell label in a UIlabel on the new ViewController.
However, When I go back to the tableview and select a different cell, the value on the new view controller doesn't update immediately. It displays the last clicked cell, then if I go back and repeat the process a second time it will update.
didSelectRowAtIndexPath
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let currentCell = tableView.cellForRowAtIndexPath(indexPath)! as UITableViewCell
self.labeltosend = currentCell.textLabel!.text!
}
prepareForSegue
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == PostSegueIdentifier {
if let destination = segue.destinationViewController as? NewViewController {
destination.newlabel = labeltosend
}
}
}
Am I supposed to reload the data somehow?
edit 1: Destination View Controller
#IBOutlet weak var dispLabel: UILabel!
var newlabel = String()
override func viewWillAppear(animated: Bool) {
dispLabel.text = newlabel
}
override func viewDidLoad() {
super.viewDidLoad()
// Do view setup here.
}

prepareForSegue(_:sender) is not called if you draw segue from cell to next view controller. Try to draw segue from view controller to view controller. And perform a manual segue using performSegueWithIdentifier("MySegueIdentifier" sender:self) in tableView(_:didSelectRowAtIndexPath:indexPath). For more info please have a look at this answer.

Think prepareForSegue is sometimes getting called before didSelectRowAtIndexPath you can move that code to prepareForSegue. When you set up a segue as you have sender in prepareForSegue is the indexPath of row tap, precisely so you can do these sorts of things.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == PostSegueIdentifier {
if let destination = segue.destinationViewController as? NewViewController {
let indexPath = sender as NSIndexPath
let currentCell = tableView.cellForRowAtIndexPath(indexPath)! as UITableViewCell
self.labeltosend = currentCell.textLabel!.text!
destination.newlabel = labeltosend
}
}
}
You should read Duncan's comment.

First: if you have your segue connected from the UITableViewCell, don't. Delete it and connect the segue from the view controller itself.
Second: on the didSelectRowAtIndexPath method, call prepareForSegue method.
And finally on the destination controller:
#IBOutlet weak var dispLabel: UILabel!
var newlabel = String()
override func viewWillAppear(animated: Bool) {
//dispLabel.text = newlabel
}
override func viewDidLoad() {
super.viewDidLoad()
// Do view setup here.
dispLabel.text = newlabel
}
Also, you need to improve your naming convention. Is really confusing
Hope this helps!

Related

Passing data from a UITableView to another ViewController depending on what row is selected

I have two ViewControllers one which contains a UITextView and the other one contains a UITableView. I would like my app to pass data for the selected row from the SecondViewController which contains the UITableView to the UITextView in the first ViewController depending on what row the user select. I am using the below code in the firstViewController (Just to give you a bit of history what I have is a UITextView inside the firstViewController and the user have the option of either entering a custom value or exert a longpressgesture then a popover Window get displayed containing the UITableView in the secondViewController. What I would like to achieve is when a row is selected from the popoverView which contains the UItableView the popoverView get closed and the value highlighted in the table get displayed in the UITextView in the firstViewController):
class ViewController: UIViewController, UITextViewDelegate, UIPopoverPresentationControllerDelegate {
#IBOutlet weak var indicativeDesignWorkingLifeTextView: UITextView!
var textInsideIndicativeDesignWorkingLifeTextView: String? = nil
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
indicativeDesignWorkingLifeTextView.text = textInsideIndicativeDesignWorkingLifeTextView
indicativeDesignWorkingLifeTextView.attributedText = placeholderTextInIndicativeDesignWorkingLifeTextView
}
}
and the below code in the secondViewController:
#objc func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
func prepare(for segue: UIStoryboardSegue, sender: UITableViewCell?) {
let toFirstViewController = segue.destination as! ViewController
// Pass the selected object to the new view controller.
if let indexPath = self.indicativeDesignWorkingLifeTable.indexPathForSelectedRow {
let selectedRow = years[indexPath.row]
toFirstViewController.textInsideIndicativeDesignWorkingLifeTextView = selectedRow
}
}
}
However, when I run the simulator and select a row from the table nothing happens inside the UITextView in the firstViewController? All what happens is that the firstViewController gets displayed. Any help is much appreciated.
Thanks,
Shadi.
Update your code as:
#objc func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.performSegue(withIdentifier: "<set identifier String>", sender: indexPath)
}
func prepare(for segue: UIStoryboardSegue, sender:Any?) {
let toFirstViewController = segue.destination as! ViewController
// Pass the selected object to the new view controller.
if let indexPath = sender as? IndexPath {
print("indexPath - \(indexPath)")
let selectedRow = years[indexPath.row]
print("selectedRow - \(selectedRow)")
toFirstViewController.textInsideIndicativeDesignWorkingLifeTextView = selectedRow
}
}

The cell I press in my table sends the label for the previously pressed cell

I am new to Swift and I have this interesting problem.
I am trying to send the label of a table cell when I segue to another view controller where I print it. The problem is that it is printing the label of the cell that was pressed previous to this press.
Here is the code in the main view controller that passes the label:
// When a user taps on a cell on the tableView, it asks for a tag name for that image.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("Cell \(indexPath) selected")
// Get cell image.
let indexPath = tableView.indexPathForSelectedRow
let currentCell = tableView.cellForRow(at: indexPath!) as! ImageFeedItemTableViewCell
imagePass = currentCell.itemImageView
labelPass = currentCell.itemTitle
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Send image to GreetingViewController.
if segue.identifier == "GoToGreeting" {
var greetingvc = segue.destination as! GreetingViewController
greetingvc.passedImage = imagePass
greetingvc.passedLabel = labelPass
}
}
and here is the relevant code in the view controller that receives the passed label:
var passedImage: UIImageView? = nil
var passedLabel: UILabel? = nil
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
print(passedLabel?.text)
}
Any help would be appreciated.
I believe prepare(for:sender:) gets called before tableView(_:didSelectRowAt:) when you hook up that segue in the storyboard.
What you can do is just use the sender parameter of the prepare(for:sender:) method to get the information you need at the right time. When a segue is triggered by a cell, as it seems to be in your case, then that cell will be the sender passed into the the prepare method. So, you could do something like:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Send image to GreetingViewController.
if let cell = sender as? ImageFeedItemTableViewCell, segue.identifier == "GoToGreeting" {
var greetingvc = segue.destination as! GreetingViewController
greetingvc.passedImage = cell.itemImageView
greetingvc.passedLabel = cell.itemTitle
}
}

Issue when performing a segue on a tableview cell

I'm currently learning Swift and trying to perform a segue when the user taps on one of the tableview cells that the app presents. At the moment, whenever the user performs this action, the next view controller is loaded successfully, but it seems that, for some reason, I cannot access any of its UI elements, as each time that I try to do it, I end up getting this error:
fatal error: unexpectedly found nil while unwrapping an Optional value
The error points to the line in which I try to modify the text of one of the labels that are displayed on the next view controller
This is the didSelectRowAt function:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){
self.performSegue(withIdentifier: "segue1", sender: self)
}
and this is the prepareForSegue function:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "segue1" {
let destinationVC = segue.destination as! UserViewController
let selectedRow = tableView.indexPathForSelectedRow!
let selectedCell = tableView.cellForRow(at: selectedRow) as! CustomCell
destinationVC.usernameLabel.text = selectedCell.userName.text //this is where the error is pointing to
destinationVC.bioLabel.text = selectedCell.bio.text
destinationVC.userImage.image = selectedCell.photo.image
}
}
I have no idea about what is causing this problem. My goal is to pass the data from the tapped cell to the next view controller, but this obviously is preventing me from doing so. Does anyone know how I can fix this? Thanks in advance.
Note: I assumed that userName and bio were both UITextFields
Why don't you try something like this?
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "segue1" {
let destination = segue.destination as! UserViewController
// Use of optional binding to make sure an indexPath exists
if let indexPath = tableView.indexPathForSelectedRow {
let cell = tableView.cellForRow(at: IndexPath(row: indexPath.row, section: indexPath.section)) as! CustomCell
// Notice how we are not directly updating the label as before.
destination.usernameText = cell.userName?.text
destination.bioText = cell.bio?.text
}
}
}
Now in UserViewController:
#IBOutlet weak var usernameLabel: UILabel!
#IBOutlet weak var bioLabel: UILabel!
// What we will be passing the text to instead.
var usernameText: String?
var bioText: String?
override func viewDidLoad() {
super.viewDidLoad()
// update the labels with the text from the proper cell.
usernameLabel?.text = usernameText
bioLabel?.text = bioText
}
You can just do the same for your image, just different types. This has to do with the outlets not being allocated when used in prepare(for segue:).
i had great issue with the prepare for segue method when trying the same thing with a UICollectionView. The 2 are very similar so you should be able to change collectionview to tableview easily.
this is what i did... using variable selectedPack
in the view controller you want to segue to you need to set the variable
// passed packName from PackViewController
var selectedPack: String!
then in the viewcontroller you are selecting the cell
public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// handle the segue to JourneyViewController with variable "selectedPack"
// not sure why you need to set the storyboard but it works
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
//create instance of the viewcontroller
let transportJourneyViewController = storyBoard.instantiateViewController(withIdentifier: "JourneyViewController") as! JourneyViewController
//value to pass - has been defined in journey
transportJourneyViewController.selectedPack = INSERT_YOUR_VALUE_TO_PASS
//present the vc
self.present(transportJourneyViewController, animated:true, completion:nil)
}
JourneyViewController is the storyboardID and ClassName of the viewcontroller you want to go to.set in the interface builder.
You'll also need to have the tableviewdatasource and tableviewdelegate defined at the top level of your view controllers and in the storyboard itself.
class JourneyViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {

Perform a segue over multiple Controller

How is it possible to perform a segue from a UITabBarController Child ViewController to a DetailView of an UITableViewController with UINavigationController in between?
There is a TabBarController with two childs, FirstView(Most Viewed Symbol) and NavigationController(Contacts Symbol).
The FirstView has a button, which should perform a segue to Show Profile VC.
The NavCont has a TableView with subclass All ProfilesTVC with a prototype cell as child.
AllProfilesTVC has an array with three names which are displayed by the reused cell.
Which viewcontroller do I have to instantiate and prepare at the function prepareForSegue in FirstView (HomeVC) and where should the segue, which I create in storyboard, direct to? So that I'm at "John's" DetailView.
And is it possible that when I performed a segue to ShowProfileVC, that I can push the Back Button to return to the All ProfilesTVC?
There is a github repo for those who want to try at github repo
FirstView / HomeVC.swift
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// what do we do here ???
}
AllItemsTVC
class AllItemsTVC: UITableViewController {
let profiles = ["Joe", "John", "Ken"]
override func viewDidLoad() {
super.viewDidLoad()
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return profiles.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! UITableViewCell
let profile = profiles[indexPath.row] as String
cell.textLabel?.text = profile
return cell
}
#IBAction func cancelFromShowProfile(segue: UIStoryboardSegue) {
}
// MARK: - Navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let identifier = segue.identifier {
switch identifier {
case "ShowProfile":
if let showProfileVC = segue.destinationViewController as? ShowProfileVC {
// pass the data to the destinationVC
let selectedProfile = profiles[tableView.indexPathForSelectedRow()!.row] as String
showProfileVC.name = selectedProfile
}
default: break
}
}
}
ShowProfileVC
class ShowProfileVC: UIViewController {
#IBOutlet weak var textLabel: UILabel! {
didSet {
textLabel.text = name
}
}
var name = "Label"
override func viewDidLoad() {
super.viewDidLoad()
}
}
Thanks for any help.
You cannot do this with a segue, but that's not a problem because you can do it quite simply in code. Just give your view controllers an identifier in storyboard and instantiated them via this identifier with the appropriate UIStoryboard API.
Start by switching to the other tab bar item in code, and then first tell the navigation controller to popToRootViewController, after which you can push all the necessary controllers onto the navigation stack in turn. You can do all the configuration you normally do in prepareForSegue just before pushing the controllers.
The trick is to do it all with animated set to false except the last step.

Swift: Pass UITableViewCell label to new ViewController

I have a UITableView that populates Cells with data based on a JSON call. like so:
var items = ["Loading..."]
var indexValue = 0
// Here is SwiftyJSON code //
for (index, item) in enumerate(json) {
var indvItem = json[index]["Brand"]["Name"].stringValue
self.items.insert(indvItem, atIndex: indexValue)
indexValue++
}
self.tableView.reloadData()
How do I get the label of the cell when it is selected and then also pass that to another ViewController?
I have managed to get:
func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
println("You selected cell #\(indexPath.row)!")
// Get Cell Label
let indexPath = tableView.indexPathForSelectedRow();
let currentCell = tableView.cellForRowAtIndexPath(indexPath!) as UITableViewCell!;
println(currentCell.textLabel.text)
}
I just cant figure out how to pass that as a variable to the next UIViewController.
Thanks
Passing data between two view controllers depends on how view controllers are linked to each other. If they are linked with segue you will need to use performSegueWithIdentifier method and override prepareForSegue method
var valueToPass:String!
func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
println("You selected cell #\(indexPath.row)!")
// Get Cell Label
let indexPath = tableView.indexPathForSelectedRow();
let currentCell = tableView.cellForRowAtIndexPath(indexPath!) as UITableViewCell!;
valueToPass = currentCell.textLabel.text
performSegueWithIdentifier("yourSegueIdentifer", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "yourSegueIdentifer") {
// initialize new view controller and cast it as your view controller
var viewController = segue.destinationViewController as AnotherViewController
// your new view controller should have property that will store passed value
viewController.passedValue = valueToPass
}
}
If your view controller are not linked with segue then you can pass values directly from your tableView function
func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
println("You selected cell #\(indexPath.row)!")
// Get Cell Label
let indexPath = tableView.indexPathForSelectedRow();
let currentCell = tableView.cellForRowAtIndexPath(indexPath!) as UITableViewCell!;
let storyboard = UIStoryboard(name: "YourStoryBoardFileName", bundle: nil)
var viewController = storyboard.instantiateViewControllerWithIdentifier("viewControllerIdentifer") as AnotherViewController
viewController.passedValue = currentCell.textLabel.text
self.presentViewController(viewContoller, animated: true , completion: nil)
}
You asked:
How do I get the label of the cell when it is selected and then also pass that to another ViewController?
I might suggest rephrasing the question as follows: "How do I retrieve the data associated with the selected cell and pass it along to another view controller?"
That might sound like the same thing, but there's an important conceptual distinction here. You really don't want to retrieve the value from the cell label. Our apps employ a MVC paradigm, so when you want to pass data information from one scene to another, you want to go back to the model (the items array), not the view (the text property of the UILabel).
This is a trivial example, so this distinction is a bit academic, but as apps get more complicated, this pattern of going back to the model becomes increasingly important. The string representation from the cell is generally is a poor substitute for the actual model objects. And, as you'll see below, it's just as easy (if not easier) to retrieve the data from the model, so you should just do that.
As an aside, you don't really need a didSelectRowAtIndexPath method at all in this case. All you need is a segue from the table view cell to the destination scene, give that segue a unique identifier (Details in my example), and then implement prepare(for:sender:):
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? DetailsViewController {
let selectedRow = tableView.indexPathForSelectedRow!.row
destination.selectedValue = items[selectedRow]
}
}
Alternatively, if your segue is between the cell and destination scene, you can also use the sender of the prepare(for:sender:):
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? DetailsViewController {
let cell = sender as! UITableViewCell
let selectedRow = tableView.indexPath(for: cell)!.row
destination.selectedValue = items[selectedRow]
}
}
But the idea is the same. Identify what row was selected, and retrieve the information from the model, the items array.
The above is Swift 3. For Swift 2.3, please see the previous version of this answer.
Okay..Its been 2 days I was searching for the answer that how could I be able to save the selected UITableViewCell label text data and display that data to an another label on an another View Controller which will come out after tapping on a cell. At last I have completed with the task and its successful. Here is the complete code with steps using Swift.I am using Xcode 6.4.
Step 1.
I have Two class assigned to the storyboard view controllers named "iOSTableViewControllerClass.swift" which is a Table View Controller and "iOSTutorialsViewControllerClass.swift" which is a normal View Controller.
Step 2.
Now make segue from iOSTableViewControllerClass to iOSTutorialsViewControllerClass by Control-dragging on the storyboard area and choose "show" from drop down menu. Click on this highlighted button according to the below image and perform the segue.
Step 3.
Now select the segue by clicking on the storyboard and give it an identifier on the Attributes Inspector. In this case I named it as "iOSTutorials"
Step 4.
Now on this step put a label on your cell as well as on the other view controller and make outlets of them on their corresponding classes.
In my case those are "#IBOutlet weak var iOSCellLbl: UILabel!" and " #IBOutlet weak var iOSTutsClassLbl: UILabel!".
Step 5.
Make a string type variable on the first Table View Controller Class. I did this as "var sendSelectedData = NSString()" also Make a string type variable on the second class. I did this as "var SecondArray:String!".
Step 6.
Now we are ready to go.
Here is the complete Code for first Class --
// iOSTableViewControllerClass.swift
import UIKit
class iOSTableViewControllerClass: UITableViewController, UITableViewDataSource,UITableViewDelegate {
// Creating A variable to save the text from the selected label and send it to the next view controller
var sendSelectedData = NSString()
//This is the outlet of the label but in my case I am using a fully customized cell so it is actually declared on a different class
#IBOutlet weak var iOSCellLbl: UILabel!
//Array for data to display on the Table View
var iOSTableData = ["Label", "Button", "Text Field", "Slider", "Switch"];
override func viewDidLoad() {
super.viewDidLoad()
//Setting the delegate and datasource of the table view
tableView.delegate = self
tableView.dataSource = self
//Registering the class here
tableView.registerClass(CustomTableViewCellClassiOS.self, forCellReuseIdentifier: "CellIDiOS")
//If your using a custom designed Cell then use this commented line to register the nib.
//tableView.registerNib(UINib(nibName: "CellForiOS", bundle: nil), forCellReuseIdentifier: "CellIDiOS")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// Return the number of sections.
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of rows in the section.
return iOSTableData.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let CellIDentifier = "CellIDiOS"
//In this case I have custom designed cells so here "CustomTableViewCellClassiOS" is the class name of the cell
var cell:CustomTableViewCellClassiOS! = tableView.dequeueReusableCellWithIdentifier(CellIDentifier, forIndexPath: indexPath) as? CustomTableViewCellClassiOS
if cell == nil{
tableView.registerNib(UINib(nibName: "CellForiOS", bundle: nil), forCellReuseIdentifier: CellIDentifier)
cell = tableView.dequeueReusableCellWithIdentifier(CellIDentifier) as? CustomTableViewCellClassiOS
}
//Here we are displaying the data to the cell label
cell.iOSCellLbl?.text = iOSTableData[indexPath.row]
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
println("You selected cell #\(indexPath.row)!")
// Get Cell Label text here and storing it to the variable
let indexPathVal: NSIndexPath = tableView.indexPathForSelectedRow()!
println("\(indexPathVal)")
let currentCell = tableView.cellForRowAtIndexPath(indexPathVal) as! CustomTableViewCellClassiOS!;
println("\(currentCell)")
println("\(currentCell.iOSCellLbl?.text!)")
//Storing the data to a string from the selected cell
sendSelectedData = currentCell.iOSCellLbl.text!
println(sendSelectedData)
//Now here I am performing the segue action after cell selection to the other view controller by using the segue Identifier Name
self.performSegueWithIdentifier("iOSTutorials", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
//Here i am checking the Segue and Saving the data to an array on the next view Controller also sending it to the next view COntroller
if segue.identifier == "iOSTutorials"{
//Creating an object of the second View controller
let controller = segue.destinationViewController as! iOSTutorialsViewControllerClass
//Sending the data here
controller.SecondArray = sendSelectedData as! String
}
Here is the complete code for the second Class..--
// iOSTutorialsViewControllerClass.swift
import UIKit
class iOSTutorialsViewControllerClass: UIViewController {
//Creating the Outlet for the Second Label on the Second View Controller Class
#IBOutlet weak var iOSTutsClassLbl: UILabel!
//Creating an array which will get the value from the first Table View Controller Class
var SecondArray:String!
override func viewDidLoad() {
super.viewDidLoad()
//Simply giving the value of the array to the newly created label's text on the second view controller
iOSTutsClassLbl.text = SecondArray
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I do it like this.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedName = nameArray[indexPath.row]
let newView: nextViewName = self.storyboard?.instantiateViewController(withIdentifier: "nextViewName") as! nextViewName
newView.label.text = selectedValue
self.present(newView, animated: true, completion: nil)
}

Resources