This question has been answered many times. But the answers I could find on it, didn't work for me, cause I can't seem to call the cell's class.
to further explain:
I have a viewController, with a UITable. The cells are configured in a UITableViewCell class. (it's from this class I need to pull the information)
my "cell class" is called mySuggestionsCel
Here's my code for "didSelectRowAtIndexPath"
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.allowsSelection = false
var selectedCell:UITableViewCell = tableView.cellForRowAtIndexPath(indexPath)!
var VC = detaiLSuggestion_VC()
self.navigationController?.pushViewController(VC, animated: true)
if selectedCell.backgroundColor == UIColor.formulaFormColor() {
selectedCell.backgroundColor = UIColor.formulaMediumBlue()
UIView.animateWithDuration(0.5, animations: {
selectedCell.backgroundColor = UIColor.formulaFormColor()
})
} else {
selectedCell.backgroundColor = UIColor.formulaGreenColor()
UIView.animateWithDuration(0.5, animations: {
selectedCell.backgroundColor = UIColor.formulaLightGreenColor()
})
}
}
I tried doing
mySuggestionsCell.someVariable
I also tried selectedCell.someVariable
neither worked.
I need to get this information from my detailSuggestion_VC(), from my cell Class. But the data it needs to pull, is the data the specific cell that's getting selected has. Which is why I'm having some trouble getting this to work.
I looked around for a while. But couldn't find any questions or answers to this specific issue.
Any help would be greatly appreciated
I'm making the following assumptions:
You have a tableViewCell class file that controls your table cells.
You have a detail-view controller that your table segues to when you tap a cell.
What you want to do is to pass the tapped cell's information so your new view controller has all the cell's info.
Instead of this:
var selectedCell: UITableViewCell = tableView.cellForRowAtIndexPath(indexPath)!
You will need to instead do a typecast into your tableViewCell class. Like so:
var selectedCell = tableView.cellForRowAtIndexPath(indexPath)! as tableViewCell
What you need to do next is to call the following function:
performSegueWithIdentifier(/*Segue Identifier goes here*/, sender: selectedCell)
Making this call will pass selectedCell's contents into sender, which can be used in prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject)
make sure you override prepareForSegue somewhere in your class.
Now, in prepareForSegue:, you can get a reference to the destinationViewController, and initialize the instance variable in the destinationViewController that will hold your selectedCell's variables.
//in prepareForSegue
let controller = segue.destinationViewController as detailSuggestion_VC
controller.cellInfo = sender
Related
I need to pass a variable containing the index of the selected row in one view to the next view.
firstview.swift
var selectedRow = 0;
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
selectedRow = indexPath.row + 1
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "viewAssignmentsSegue") {
let navController = segue.destinationViewController as! UINavigationController
let controller = navController.viewControllers[0] as! AssignmentsViewController
print(selectedRow)
controller.activeCourse = selectedRow
}
}
My issue is that, when a row is selected, the selectedRow variable isn't updated by the tableView method before the segue occurs, meaning that the value is essentially always one behind what it should be. How can I delay the prepareForSegue until the variable is updated or how else can I successfully pass the selected row to the next view without delay?
One possibility: Don't implement didSelectRowAtIndexPath:. Just move that functionality into your prepareForSegue implementation. That, after all, is what is called first in response to your tapping the cell. Even in prepareForSegue you can ask the table view what row is selected.
Another possibility: Implement willSelectRowAtIndexPath: instead of didSelectRowAtIndexPath:. It happens earlier.
in your prepareForSegue:
controller.activeCourse = self.tableView.indexPathForSelectedRow
don't wait for didSelect to be called - react earlier.
Use this on prepareForSegue:
let path = self.tableView.indexPathForSelectedRow()!
controller.activeCourse = path.row
or
let path = self.tableView.indexPathForCell(sender)
controller.activeCourse = path.row
I have a tableView with 7 rows, each row is a different action. When a user taps on a row, a view controller is shown as a popover and a text field is displayed for them to add a note. I then want to save this note to Parse but I need to save the passedChildID with it and therefore it (passedChildID) needs to be passed from the tableviewcontroller. I have this working in other area's of my app from one tableView to another tableView but for some reason it won't work with the popover.
The following code is in SingleChildViewTableViewController.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "childToNote" {
let popoverViewController = segue.destinationViewController
popoverViewController.modalPresentationStyle = UIModalPresentationStyle.Popover
popoverViewController.popoverPresentationController!.delegate = self
let noteActionViewController = segue.destinationViewController as! actionNoteViewController
if let indexPath = tableView.indexPathForCell(sender as! UITableViewCell) {
noteActionViewController.passedChildID = passedChildID
}
}
if segue.identifier == "childToItemNeeded" {
//other stuff
}
}
This builds and runs fine, but when I tap on that particular row, I get the following error.
Could not cast value of type '<<app_name>>.SingleChildTableViewController' (0x10e1bef60) to 'UITableViewCell' (0x110bca128).
I have tried moving the let noteActionViewController... code above the let popoverViewController figuring that the former was never getting run but that didn't change anything.
The segue is being performed as followed (in case it makes any difference).
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if indexPath.row == 0 {
performSegueWithIdentifier("childToNote", sender: self)
}
}
I'm stumped because this code has worked elsewhere in my app without fail.
it looks like your problem lies in this line
indexPathForCell(sender as! UITableViewCell)
the sender in this case you are telling it to be self, which is a UIViewController
performSegueWithIdentifier("childToNote", sender: self)
You are then telling in the indexPathForCell line to cast it as a UITableViewCell but its of type SingleChildTableViewController. So change the sender to be the UITableViewCell
Maybe you can save the indexPath as a local variable and when you select a cell you change the value of the variable.
I have two files, MyTableViewController and myViewController. I set UIImageView on TableCell in MyTableVIewController. myViewController does not contain anything. And I created an array called ImageArray which contains an array of Images.
What I am aiming to do here is when I click an image on TableCell in myTableViewController, I want the clicked image to appear in the myViewController. And some description about the clicked image beside the image too. I want to use myViewController for users to get detailed information of the selected image.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("Cell") as! UITableViewCell
var ImageView = cell.viewWithTag(1) as! UIImageView
ImageView.image = UIImage(named: ImageArray[indexPath.row])
ImageView.contentMode = .ScaleAspectFit
var TextView = cell.viewWithTag(2) as! UILabel
TextView.text = ImageArray[indexPath.row]
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
performSegueWithIdentifier("next", sender: indexPath)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "next") {
let destination = segue.destinationViewController as! myViewController
}
}
I don't know what to do to make it happen. I really appreciate if you could help me figure out! Thanks!
First and foremost, I'm assuming your MyTableViewController class conforms to both UITableViewDataSource and UITableViewDelegate protocols and that you've set your MyTableViewController class to be the delegate in code or via Storyboard.
With that sorted out,there are multiple ways to achieve the result you seek.
You can declare your ImageArray in an independent class and call it inside your MyTableViewController class, index them onto a tableView using the tableView delegate methods, and finally using the prepareForSegue method to push your images onto your myViewController. Or you can simply declare and initialize your ImageArray at the top of your MyTableViewController class like below:
var ImageArray = [("Moscow Russia.jpg", "Europe"),
("London England.jpg", "Europe")]
In the ImageArray above, ensure that your image name matches exactly as the asset name you've imported into your Xcode project.
Then we specify how many rows in section we need ImageArray to occupy on our tableView (i.e. basically count our ImageArray into our TableView) with below required method:
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return ImageArray.count ?? 0
}
Next, you want to present your ImageArray in each row of of the cell using the tableView:cellForRowAtIndexPath: method.
Side Note on your TableCell: Hopefully your TableCell is subclassed from UITableViewCell and you have already declared and connected two IBOutlets, say, imageView and textLabel respectively. Also, ensure your TableCell is properly linked to your prototype cell in Storyboard under Identity Inspector) Your TableCell class should look something like below:
import UIKit
class CustomTableViewCell: UITableViewCell {
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var textLabel: UILabel!
}
Now back into your MyTableVIewController class. From your code, I see you're casting the line 'let cell = ...' as 'UITableViewCell. You should rather cast it as 'TableCell' instead since you're subclassing it. Implement the tableView:cellForRowAtIndexPath: method as follows:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = self.tableView.dequeueReusableCellWithIdentifier("Cell") as! TableCell
//Note that I'm using tuples here. Pretty cool huh. Got to love Swift!
let (imageName, textNameToGoWithImage) = ImageArray[indexPath.row]
cell.textLabel.text = textNameToGoWithImage
cell.imageView.image = UIImage(named: imageName)
cell.imageView.contentMode = .ScaleAspectFit
// You could have also used 'if let' in optional binding to safely unwrap your image if you want like below.
// if let image = UIImage(named: imageName){
// cell.imageView?.image = image
// cell.imageView.contentMode = .ScaleAspectFit
// }
return cell
}
It looks like you're a little confused about when and where to use performSegueWithIdentifier method as opposed to using -prepareForSegue method. And even when to use the tableView:didSelectRowAtIndexPath method.
Let me briefly explain here. You use the performSegueWithIdentifier method when you didn't control-drag a segue from one ViewController's scene to another in Storyboard. This way, using the performSegueWithIdentifier method will allow you to move between ViewController scenes as long as you specify the right identifier which you've set in Storyboard under 'Attributes Inspector.'
Now if you're using Storyboard instead, you wouldn't need the tableView:didSelectRowAtIndexPath method. What the tableView:didSelectRowAtIndexPath method does is that it tells the delegate that the specified row is now selected and we can do something within its code body (like push an image or a text onto another ViewController Scene like you're trying to do). But that becomes redundant when you use segues. All you have to do, is to control-drag a segue from the table cell on your MyTableViewController scene to your myViewController scene. Choose 'Show' and give the segue an identifier name like you've done "next". (A little side note: if you want the Back button functionality to display at top navigator bar when you run your app, you simply embed your MyTableViewController in a UINavigationController to give you that 'Back' button functionality. With your MyTableViewController Scene selected in Storyboard, Go to the top menu and select Editor >> Embed In >> Navigation Controller. Then walla!!)
Lets now go ahead and implement our tableView:prepareForSegue method like below:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "next" {
//Note that, originally, destinationViewController is of Type UIViewController and has to be casted as myViewController instead since that's the ViewController we trying to go to.
let destinationVC = segue.destinationViewController as! myViewController
if let indexPath = self.tableView.indexPathForSelectedRow{
let selectedRow = ImageArray[indexPath.row]
destinationVC.imageName2 = selectedRow.0
destinationVC.textName2 = selectedRow.1
}
From the above code, make sure you set the 'imageName' and 'textName' as properties in your myViewController class first before you can access them with 'destinationVC' which is now of type myViewController. These two properties will hold the data we are passing from MyTableViewController class to myViewController class. And we are using the array index to pass data to these two properties accordingly.
You can then create two IBOutlets to display your image and text by passing these set 'imageName2' and 'textName2' properties to your outlets (or any UI control for that matter).
Now the reason why you will have to set properties first in
myViewController class before you pass them on or around (i.e. to a
UI element, closure, another VC etc) is that, when you hit a tableView cell from
MyTableViewController scene to segue onto your next ViewController
scene (i.e. your myViewController scene), iOS hasn't instantiated
that second scene just yet. And so you need a property to hold onto the data
you're trying to pass onto your second scene View Controller first so that you can
make use of it later when that class finally loads.
So your myViewController class should look something like below:
import UIKit
class myViewController : UIViewController {
//Your two strings to initially hold onto the data
//being passed to myViewController class
var imageName2 : String?
var textName2 : String?
#IBOutlet weak var detailImageView: UIImageView!
#IBOutlet weak var detailTextNameLabel: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
detailTextNameLabel.text = textName2!
if let image = UIImage(named: imageName2!) {
self.detailImageView.image = image
}
}
And that's it!
Things to note on labelling and conventions in Swift:
Start naming classes with block letters (i.e. class
ViewController() {})
Classes and properties should capture the meaning of what they
represent. I will recommend you change your MyTableViewController
and 'myViewController'classes accordingly to reflect what they truly
mean or do (You can go with 'MainTableViewController' and 'DetailViewController'. That will do just fine).
Use camelToe labelling for properties and methods. (I used the labels you
provided in your question in order not to confuse you too much).
Enjoy!
This should help out:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "next") {
let destination = segue.destinationViewController as! myViewController
destination.imageView.image = UIImage(named: ImageArray[tableView.indexPathForSelectedRow?.row])
destination.textView.text = ImageArray[tableView.indexPathForSelectedRow?.row]
}
}
(Where imageView and textView are views in your new viewController.)
Note:
tableView.indexPathForSelectedRow?.row should give you the selected row, as the name implies, but it can be nil, so be careful.
In addition, Swift variable naming conventions are camelCase, so imageView is the correct way, while ImageView is incorrect.
In swift 3 you can do something like this:
In your MyTableViewController class:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! UITableViewCell
// for imageview
var ImageView : UIImageView = cell.contentView.viewWithTag(1) as! UIImageView
ImageView.image = UIImage(named: ImageArray[indexPath.row])
// for text
var TextView : UILabel = cell.contentView.viewWithTag(2) as! UILabel
ImageView.text = ImageArray[indexPath.row]
return cell
}
And in your didSelectRow method:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
let nextVC = self.storyboard?.instantiateViewController(withIdentifier: "myViewController") as! myViewController
nextVC.myImgView.image = UIImame(named: ImageArray[indexPath.row])
nextVC.myLabel.text = ImageArray[indexPath.row]
self.present(nextVC!, animated: true)
}
And inmyViewController class:
class myViewController: UIViewController
{
let myImageView = UIImageView()
let myLabel = UILabel()
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath
indexPath : NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier ("ChecklistItem") as UITableViewCell
let label = cell.viewWithTag(1000) as UILabel
if indexPath.row == 0 {
label.text = "jaswanth"
} else if indexPath.row == 1 {
label.text = "nikhil"
} else if indexPath.row == 2 {
label.text = "krishna"
}
return cell
}
I have UITableViewController, I have used above code to display the cells in the table and I had another UIViewController when user taps on jaswanth the text(labels text) in UIViewController have be changed and the same way when nikhil is tapped the text(labels text) in UIViewController have changed
I had only one UITableViewController and one UIViewController how can I do this?
You would want to be able to detect when these cells in the table are pressed. This can be done using the recommendation by Mike Welsh through implementing the method: func tableView(_ tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
This allows you to be able to tell which cell is selected from the NSIndexPath.
From here on, it seems to me that you want to be able to change the text (on a button or field) that is controlled by another UIViewController. One way it can be done is to have a reference to this UIViewController and creating a method to update this text field.
If the UITableViewController is a child view controller, it can be as simple as calling
let parent = parentViewController!
Else if the Controllers are related by a Segue, it can also be done by overriding prepareForSegue of the original ViewController.
By using the segue.identifier to map to the segue you have made in StoryBoard/programmatically, you are able to set a delegate/pointer to the original UIViewController.
override func prepareForSegue(_ segue: UIStoryboardSegue, sender sender: AnyObject?) {
if segue.identifier == "SomeStringID" {
self.tableViewController = segue.destinationViewController as? UITableViewController
self.tableViewController.delegate = self
}
}
With the reference to the other ViewController, you should be able to define your own method to change this text field quite easily
Hope this helps you!
What I'm trying to do is to capture the textLabel.text of a cell in my table view controller, and pass it to a label in another view. After a lot of research here's what I have so far:
var valueToPass:String!
override 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
println("\(valueToPass) is captured")
performSegueWithIdentifier("theSegue", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "theSegue") {
var viewController = segue.destinationViewController as FinalView
viewController.resto = valueToPass
}
}
And here is the code for FinalView, which is where the variable 'resto' lives:
class FinalView: UIViewController{
var resto:String!
override func viewDidLoad() {
super.viewDidLoad()
println("the restoname is: \(resto)")
restoLabel.text = resto
}
#IBOutlet var restoLabel: UILabel!
}
but when I check whether everything worked correctly, this is the result of my println statements:
the restoname is: nil
You selected cell #13!
Tanad Thai is captured
Why is the string 'Tanad Thai' not passing over to 'rest' in the FinalView class? I really appreciate all the help I can get.
You have done an excellent job instrumenting your code, but you need to think about what you have learned. Look at the output of your instrumentation:
the restoname is: nil
You selected cell #13!
Tanad Thai is captured
That output proves that FinalView's viewDidLoad is being called before the table view controller's didSelectRowAtIndexPath: is called. Thus, you cannot use didSelectRowAtIndexPath: to set a value on which viewDidLoad depends! You need to find an earlier moment. Personally, I would suggest doing all the work in prepareForSegue.
Also, I'm a little worried that you may be performing the segue twice - once in the storyboard and once in code. If you have set up the segue to emanate from the cell in the storyboard, you do not need to call performSegueWithIdentifier - it will be called automatically.