Trouble passing a custom cell value to new viewController in swift - uitableview

I have a tableview that uses custom cells. Problem is I do not know how to pass the value of a textField in my custom cell to the next view controller by using prepareForSegue. The code I am using is:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath, object: PFObject) -> PFTableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("StaffCell") as StaffCustomCell!
if cell == nil {
cell = StaffCustomCell(style: UITableViewCellStyle.Default, reuseIdentifier: "StaffCell")
}
// Extract values from the PFObject to display in the table cell
cell?.staffNic?.text = object["Nic"] as String!
cell?.staffApellido?.text = object["Apellido"] as String!
var initialThumbnail = UIImage(named: "iboAzul")
cell.staffFoto.image = initialThumbnail
if let thumbnail = object["FotoStaff"] as? PFFile {
cell.staffFoto.file = thumbnail
cell.staffFoto.loadInBackground()
}
return cell
}
// Pass the custom cell value to the next view controller
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "segueStaffSeleccionado" {
let detailViewController = segue.destinationViewController.visibleViewController as StaffDetailViewController
// This is the code I have no idea how to write. I need to get a value from the selected customCell
}
Any ideas? Thanks a lot

You get the selected cell via tableView.indexPathForSelectedRow. With that indexPath you have access to the cell:
// Pass the custom cell value to the next view controller
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "segueStaffSeleccionado" {
let detailViewController = segue.destinationViewController.visibleViewController as StaffDetailViewController
if let indexPath = self.tableView.indexPathForSelectedRow() {
let cell = self.tableView.cellForRowAtIndexPath(indexPath)
// path the cell's content to your detailViewController
detailViewController.myProperty = cell.textLabel?.text
}
}
Another solution: If the segue is performed directly from the tableViewCell (by ctrl-dragging the segue from the cell in InterfaceBuilder) then the sender is the cell:
// Pass the custom cell value to the next view controller
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
...
if let cell = sender as StaffCustomCell {
// path the cell's content to your detailViewController
detailViewController.myProperty = cell.textLabel?.text
}
}

Related

Move Text from cell to text label in a different view controller

I have created an app that displays 10 random numbers in a table view. This is the code I used
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ShowNumber", for: indexPath)
// Configure the cell...
cell.textLabel?.text = "\(indexPath.row + 1): \(Int(arc4random_uniform(10001)))"
return cell
}
Now I want to be able to click on one cell and be taken to another view controller and in the center of the page it displays the random number. I have the view controller all set up and linked to the original table view. I'm just having trouble passing my data through the segue. This is what I have so far. I know it unfinished, I just don't know what to do.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showRandomNumber" {
let controller = segue.destination as! ShowNumberTableViewController
let selectedRow = (tableView.indexPathForSelectedRow as NSIndexPath?)?.row ?? 0
controller.LabelText =
}
The LabelText is the name of the label that I want to pass the data to, but I don't know how to
Using the indexPathForSelectedRow, you can get a reference to the cell that triggered the segue. You can then access the textLabel within that cell, get its value, and pass it to the next view controller.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showRandomNumber" {
let controller = segue.destination as! ShowNumberTableViewController
if let indexPath = tableView.indexPathForSelectedRow {
let cell = tableView.cellForRowAtIndexPath(indexPath) as UITableViewCell
if let textToPass = cell.textLabel.text {
controller.labelText = textToPass
}
}
}
}
Then in ShowNumberTableViewController you want a var labelText and in your viewDidLoad() you can assign it to the textLabel in that view controller with
centerLabel.text = labelText
The way you are doing, the numbers will change when you scroll the table view. I think you should save the numbers in an array.
About how to grab the selected number in the prepareForSegue: I would do this way:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showRandomNumber" {
if let cell = sender as? UITableViewCell, indexPath = tableView.indexPathForCell(cell) {
let selectedNumber = numbers[indexPath.row]
controller.labelText = String(selectedNumber)
}
}
}

Pass data from a button in reused custom cell

I'm having trouble passing data from a custom cell by a user tapping a button in that custom cell. I sometimes get the wrong cells data since the cell is being reused. I was wondering if there was a full proof way to always get the right cell data to its button in each cell no matter which cell is currently on the screen. Below is my code. Any help is greatly appreciated.
My Custom Cell:
protocol CustomCellDelegate {
func segueWithCellData()
}
class CustomTableViewCell : UITableViewCell {
var delegate = CustomCellDelegate?
#IBAction func buttonTapped() {
if let delegate = self.delegate {
delegate.segueWithCellData()
}
}
}
MyTableViewController:
class MyTableViewController : UITableViewController, CustomCellDelegate {
var posts = [Post]()
var title: String!
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let post = posts[indexPath.row]
let cell = tableView.dequeueReusableCellWithIdentifier("CustomCellReuseIdentifier", forIndexPath: indexPath)
title = post.title
cell.delegate = self
return cell
}
func segueWithCellData() {
self.performSegueWithIdentifier("passMyData", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == “passMyData” {
let destination = segue.destinationViewController as! UINavigationController
let targetVC = destination.topViewController as! nextVC
targetVC.title = title
}
}
}
My Custom Cell:
protocol CustomCellDelegate {
func segueWithCellData(cell:CustomTableViewCell)
}
class CustomTableViewCell : UITableViewCell {
var delegate = CustomCellDelegate?
#IBAction func buttonTapped() {
if let delegate = self.delegate {
delegate.segueWithCellData(self)
}
}
}
CustomCellDelegate Method:
func segueWithCellData(cell:CustomTableViewCell) {
//Get indexpath of selected cell here
let indexPath = self.tableView.indexPathForCell(cell)
self.performSegueWithIdentifier("passMyData", sender: self)
}
Hence, no need of tagging cell.
Since, you have indexPath of the selected cell, you can get data from this and pass this through sender parameter of performSegueWithIdentifier method.
For example,
func segueWithCellData(cell:CustomTableViewCell) {
//Get index-path of selected cell here
let selectedIndexPath = self.tableView.indexPathForCell(cell)
let post = posts[selectedIndexPath.row]
self.performSegueWithIdentifier("passMyData", sender: post)
}
and, get the data inside prepareForSegue as follows:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == “passMyData” {
let destination = segue.destinationViewController as! UINavigationController
let targetVC = destination.topViewController as! nextVC
//Get passed data here
let passedPost = sender as! Post
targetVC.title = title
}
}
Full proof solution which i have used in almost all apps. Create a custom property of type NSIndexPath in a category class of UIButton and assign the indexPath in cellForRowAtIndexPath function. Now in the callback of the button find the object at index by the buttons indexPath.row from the datasource. this never fails.
first you have to create a dictionary of index and titles like this in MyTableViewController:
var titleDict = [Int:String]()
set the tag of the cell to index in table view and append title to titleDict like this in MyTableViewController:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let post = posts[indexPath.row]
let cell = tableView.dequeueReusableCellWithIdentifier("CustomCellReuseIdentifier", forIndexPath: indexPath)
title = post.title
let index = indexPath.row
cell.tag = index
titleDict[index] = title
cell.delegate = self
return cell
}
and pass the tag value of that cell in cell delegate method like this in My Custom Cell:
protocol CustomCellDelegate {
func segueWithCellData(index:Int)
}
class CustomTableViewCell : UITableViewCell {
var delegate = CustomCellDelegate?
#IBAction func buttonTapped() {
if let delegate = self.delegate {
let index = self.tag
delegate.segueWithCellData(index)
}
}
}
and access the title from the titleDict with the given index from delegate method and set to title variable in MyTableViewController:
func segueWithCellData(index:Int) {
if let title = titleDict[index]{
self.title = title
}
self.performSegueWithIdentifier("passMyData", sender: self)
}
Simple solution: fill the tableView from an array (String) and update the tableView. If you want change some datas in the tableView you need to update your array and refresh the tableView.
I use this solution in my applications and it works great.

Swift 2.1 - How to pass index path row of collectionView cell to segue

From the main controller that I have integrated collection view, I want to pass selected cell index path to another view controller (detail view)
so I can use it for updating a specific record.
I have the following working prepareForSegue
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "RecipeDetailVC" {
let detailVC = segue.destinationViewController as? RecipeDetailVC
if let recipeCell = sender as? Recipe {
detailVC!.recipe = recipeCell
}
}
}
And I've tried including let indexPath = collection.indexPathForCell(sender as! UICollectionViewCell) but I get Could not cast value of type 'xxx.Recipe' (0x7fae7580c950) to 'UICollectionViewCell' at runtime.
I also have performSegueWithIdentifier("RecipeDetailVC", sender: recipeCell) and I wonder if I can use this to pass the selected cell's index path but not sure I can add this index to the sender.
I am not clear about the hierarchy of your collectionViewCell. But I think the sender maybe not a cell. Try to use
let indexPath = collection.indexPathForCell(sender.superView as! UICollectionViewCell)
or
let indexPath = collection.indexPathForCell(sender.superView!.superView as! UICollectionViewCell)
That may work.
I've wrote up a quick example to show you, it uses a tableView but the concept is the same:
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var things = [1,2,3,4,5,6,7,8] // These can be anything...
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell()
let objectForCell = self.things[indexPath.row] // Regular stuff
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let objectAtIndex = self.things[indexPath.row]
let indexOfObject = indexPath.row
self.performSegueWithIdentifier("next", sender: indexOfObject)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "next" {
// On this View Controller make an Index property, like var index
let nextVC = segue.destinationViewController as! UIViewController
nextVC.index = sender as! Int
}
}
}
Here you can see you get the actual object itself and use it as the sender in the perform segue method. You can access it in prepareForSegue and assign it directly to a property on the destination view controller.

UIImage not appearing

When I segue my UIImageView from my NewsTableViewController.swift, the image does not appear in my NewsDetailTableViewController.swift.
Here is an image of my simulator:
Here is an image of my Main.storyboard:
Here is my prepareForSegue() method code:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?){
if segue.identifier == "showDetail" {
if let indexPath = tableView.indexPathForSelectedRow {
let destinationController = segue.destinationViewController as! NewsDetailTableViewController
destinationController.item = items[indexPath.row]
let newsImage = UIImageView(image: UIImage(named: detailImages[indexPath.row]))
destinationController.image = newsImage
}
}
}
It is also worth mentioning that my image in my NewsTableViewController.swift is set to 0x0, and in my NewsDetailTableViewController.swift, it is set to 600x216. I don't know if this would matter or not. If you desire any code, please do feel free to ask.
FYI, I use Swift.
In prepareForSegue the IBOutlets of your destinationController have not been initialized yet. Just pass detailImages to another array property on NewsDetailTableViewController. Change your prepareForSegue to:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?){
if segue.identifier == "showDetail" {
if let indexPath = tableView.indexPathForSelectedRow {
let destinationController = segue.destinationViewController as! NewsDetailTableViewController
destinationController.item = items[indexPath.row]
destinationController.imageNames = detailImages
}
}
}
Then in NewsTableViewController add the following code:
var imageNames:[String]?
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
//replace with whatever identifier you're using
var cell:UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("Cell") as UITableViewCell
cell.imageView?.image = self.imageNames[indexPath.row]?
return cell
}

Button Segue in custom UITableViewCell

I have a custom UITableViewCell with a button inside to segue the data to another viewController. This is by utilizing the
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
//cell setup code
cell.button.tag = indexPath.row
cell.button.addTarget(self, action: "editRow", forControlEvents: .TouchUpInside)
return cell
}
then using the function
func editRow() {
self.performSegueWithIdentifier("editRow", sender: self)
}
the editRow segue is a storyboard segue.
The issue...Instead of performing the segue and passing the data, it fires the segue twice and doesn't transfer any data to the "SLEdit" viewController. Any ideas as to why this is happening? Please keep in mind I'm not very familiar with objective-c due to beginning learning with swift. If you need any further information or more code let me know. Thanks!
EDIT:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "editRow" {
let cell = sender as! SLTableViewCell
let indexPath = tableView.indexPathForCell(cell)
let SListController:SLEdit = segue.destinationViewController as! SLEdit
let items:SList = frc.objectAtIndexPath(indexPath!) as! SList
SListController.item = items
}
}
This is because you have already made a segue action in storyboard editor and you are calling the same segue again by assigning custom action to the button.
And to pass data to the next view controller use this method:
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!){
if segue.identifier == "segueIdentifier" {
}
}
Edit 1:
The error: "Could not cast value of type UIButton" is due to this line:
let cell = sender as! SLTableViewCell
Here the sender UIButton and you are convertring/assigning(whichever term is suitable) to the TableViewCell.
Edit 3:
Replace your code by this:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!{
if segue.identifier == "editRow" {
let button = sender as! UIButton
let view = button.superview!
let cell = view.superview as! <Your custom cell name here>
let indexPath = itemTable.indexPathForCell(cell)
let SListController:SLEdit = segue.destinationViewController as! SLEdit
let items:SList = frc.objectAtIndexPath(indexPath) as! SList
SListController.item = items
}
}

Resources