I am trying to present a viewController depending on which tableView cell is clicked.
Currently if they are all clicked they redirect to the same viewController, how would I alter this?
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! TableViewCell
cell.cellLabel.text = self.objects.objectAtIndex(indexPath.row) as! String
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier("other", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "other"){
var upcoming: otherViewController = segue.destinationViewController as! otherViewController
let indexPath = self.tableView.indexPathForSelectedRow!
let titleString = self.objects.objectAtIndex(indexPath.row) as! String
upcoming.titleString = titleString
self.tableView.deselectRowAtIndexPath(indexPath, animated: true)
}
}
Could I do an if else on the cellLabel.text? as in if (cellLabel.text == "option8"){
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier("option8", sender: self)
}
}
I am using Swift and xcode7.
Don't and never do comparision with cellLabel.text, consider using indexPath.row for that. Then adding different segues in your storyboard for each view controller you need. Code will be something like that:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if indexPath.row == 0 || indexPath.row == 2{
self.performSegueWithIdentifier("other", sender: self)
}
else{
self.performSegueWithIdentifier("other", sender: self)
}
}
Yes, you can.
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let cell = tableView.cellForRowAtIndexPath(indexPath)
if cell.cellLabel.text == "option8" {
self.performSegueWithIdentifier("other8", sender: self)
}
}
You can check indexpath.row in didSelectrow according to row no you can redirect to perticular controller.
Related
I am making an app where you create memories into a tableView and you tap on a cell and it takes you to its designated VC, I am trying to make it so that when the cell is tapped, the text from inside that table cell is taken into the label in the next VC. This works but the problem is that when the view is first opened, the text doesn't appear inside the label, even though the console says that the text has loaded correctly:
TableView with a test cell in it.
First time the VC for the cell is opened
Console saying that the text has transferred and loaded correctly
Second time the VC is opened.
Here is my transferring and loading code for the ViewControllers:
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate {
var selectedRow : Int = 0 + 1
var selectedNameVC : String?
var favourites: [NSManagedObject] = []
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
//MARK:- New CoreData methods
let favMemory = favourites[indexPath.row]
cell.textLabel?.text = favMemory.value(forKeyPath: "name") as? String
if #available(iOS 13.0, *) {
cell.backgroundColor = UIColor.secondarySystemGroupedBackground
} else {
// Fallback on earlier versions
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
selectedRow = indexPath.row
let cell = tableView.cellForRow(at: indexPath)
let favMemory = cell?.textLabel?.text
selectedNameVC = favMemory
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? FavouritesViewController {
destination.row = selectedRow
destination.selectedName = selectedNameVC
}
}
class FavouritesViewController: UIViewController, UINavigationControllerDelegate, UIImagePickerControllerDelegate, UITextFieldDelegate, UITextViewDelegate {
var selectedName: String?
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
labelText.text = selectedName
if labelText.text == "" as String? {
print("Title has not loaded correctly.")
} else {
print("Title has loaded correctly.")
}
}
First, you shouldn't get values from cells... get them from your data source (which is what you used to set the values).
So, in didSelectRowAt instead of:
let cell = tableView.cellForRow(at: indexPath)
let favMemory = cell?.textLabel?.text
you should do:
let favMemory = favourites[indexPath.row]
However, that's not what's causing your issue.
I'm assuming in your Storyboard you have a "Show Segue" connected from your prototype cell to your FavouritesViewController. If that's the case, prepare(for segue: ...) is called before didSelectRowAt. To confirm this, change your code to:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
selectedRow = indexPath.row
// don't do this
//let cell = tableView.cellForRow(at: indexPath)
//let favMemory = cell?.textLabel?.text
let favMemory = favourites[indexPath.row]
print("In didSelectRowAt --- Setting var selectedNameVC = favMemory: \(favMemory)")
selectedNameVC = favMemory
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? FavouritesViewController {
print("In prepareForSegue --- Setting selectedName in FavouritesViewController = selectedNameVC: \(selectedNameVC)")
destination.row = selectedRow
destination.selectedName = selectedNameVC
}
}
This is what you should see in the debug console:
In prepareForSegue --- Setting selectedName in FavouritesViewController = selectedNameVC: nil
In didSelectRowAt --- Setting var selectedNameVC = favMemory: Test
Title has loaded correctly.
As you see, prepareForSegue runs first, so the value of selectedNameVC is nil (it hasn't been set yet).
To fix this, you could set your variables in willSelectRowAt instead of in didSelectRowAt:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
selectedRow = indexPath.row
let favMemory = favourites[indexPath.row]
selectedNameVC = favMemory
return indexPath
}
Alternatively, create your Segue in Storyboard, but do not connect it to the cell. Instead, give it an Identifier (such as "ShowFavourites"), and in didSelectRowAt, set your vars and call performSegue:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
selectedRow = indexPath.row
let favMemory = favourites[indexPath.row]
selectedNameVC = favMemory
performSegue(withIdentifier: "ShowFavourites", sender: nil)
}
How to make Each Cell open the specific view for its indexpath.
In tableview didselect what should i do to make each cell open as its own indexpath so each cell have a different data in the next view
i am tryin when click in a cell in the tableview the next view present it self with it's own data as the next view contain a uitextview like in note app
what should i apply at row didselect
// MARK: -TableFunctions
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return SavingTasks.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let newtask = self.SavingTasks[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
cell.TheLabel?.text = newtask
return cell
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if(editingStyle == .delete)
{
self.SavingTasks.remove(at: indexPath.row)
self.TasksTable.deleteRows(at: [indexPath], with: .fade)
GetData()
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let NewIndex = self.SavingTasks[indexPath.row]
let view = self.storyboard?.instantiateViewController(withIdentifier: "TaskDetail") as! TaskDetail
view.SavingDetails = [NewIndex]
view.Index = indexPath.row
self.navigationController?.pushViewController(view, animated: true)
}
next class should be appeared
class TaskDetail: UIViewController {
var Delegate: NoteDetailDelegate!
var SavingDetails = [String]()
var Index: Int?
#IBOutlet weak var TaskDetailsFiled: UITextView!
#IBAction func SaveTDF(_ sender: UIButton) {
UserDefaults.standard.set(TaskDetailsFiled.text, forKey: "Saved")
self.navigationController?.popViewController(animated: true)
}
You can use a segue and prepare(for:sender:) to get the next view controller ready more easily than instantiating the view controller and popping it via code. Official documentation here and a sample app from Apple here
An implementation example:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "mysegue"{
if let nextViewController = segue.destination as? NextViewController{
nextViewController.Index = 2
}
}
}
A highlight from the official doc:
For example, if the segue originated from a table view, the sender parameter would identify the table view cell that the user tapped
If you want to stick with the code implementation, you can call view.myvariable = myvalue in your didSelect
Could not cast value of type 'Google_Books_1.BookTableViewCell' (0x105557600) to 'NSIndexPath' (0x1061cb438)
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("BookCell", forIndexPath: indexPath) as!BookTableViewCell
...
}
func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
// let contact = ContactList![indexPath.row]
performSegueWithIdentifier("BookDetailSegue", sender: indexPath)
}
func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
// let contact = ContactList![indexPath.row]
performSegueWithIdentifier("BookDetailSegue", sender: indexPath)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "BookDetailSegue" {
let vc = segue.destinationViewController as! BookDetailViewController
let indexPath = sender as! NSIndexPath
vc.book = self.bookList[indexPath.row] **//error is here**
vc.index = indexPath.row
}
}
How to handle such error?
Why not base yourself on your tableView's indexPathForSelectedRow property instead?
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "BookDetailSegue" {
let vc = segue.destinationViewController as! BookDetailViewController
if let indexPath = tableView.indexPathForSelectedRow {
vc.book = self.bookList[indexPath.row] **//error is here**
vc.index = indexPath.row
}
}
}
Declare an instance variable
var indexPath: NSIndexPath?
Then assign selected indexPath like bellow:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
// let contact = ContactList![indexPath.row]
self.indexPath = indexPath
performSegueWithIdentifier("BookDetailSegue", sender: indexPath)
}
Now access the indexPath like this:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "BookDetailSegue" {
let vc = segue.destinationViewController as! BookDetailViewController
vc.book = self.bookList[self.indexPath.row!] **//error is here**
vc.index = indexPath.row
}
}
Hope this help you.
I am trying to pass cell's tag data to another viewcontroller's variable
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let currentCell = tableView.cellForRowAtIndexPath(indexPath) as UITableViewCell!
print(currentCell.tag)
valueToPass = currentCell.tag
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let Dest = segue.destinationViewController as! EventDetail
if (segue.identifier == "eDetay") {
Dest.eventid = String(valueToPass)
}
}
When i try as it is, valueToPass is always passed the previous data.
Is there a way to get selected cell data from prepareForSegue ?
What is the best way to do that?
You are overriding the indexPath parameter that is being passed to you.
Remove the first let statement. Use the indexPath that is being passed to you.
func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!)
{
let cell = tableView.cellForRowAtIndexPath(indexPath) as YourCell
//cell.value, cell.text, cell.randomValue or whatever you need
}
I need to pass a string with prepareForSegue from a tableView disclosure link. Here is the prepareForSegue code I use. This is the code I found in several cases when other users needed to do the same thing, but in my case it doesn't work. Problem is with indexPathForSelectedRow() that is nil:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "alertsDetail" {
let detailAlertViewController = segue.destinationViewController as DetailAlertViewController
println(alertTable.indexPathForSelectedRow()) // NIL
if let indexPath = alertTable.indexPathForSelectedRow() {
let entryToPass = unpublishedPhotosObjectId[indexPath.row]
detailAlertViewController.entryId = entryToPass
}
}
}
I named the tableView "alertTable" and not self because the tableview is not a view controller, just a view.
In case needed, here is tableview code:
#IBOutlet weak var alertTable: UITableView!
/////////////TableView - START
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return unpublishedPhotos.count
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 170
}
func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
tableView.separatorColor = UIColor.clearColor()
let cell = tableView.dequeueReusableCellWithIdentifier("adminAlertsCell") as? adminAlertsCell
var data = unpublishedPhotos[indexPath.row].getData()
let image = UIImage(data: data)
cell!.myImageInCell.image = image
return cell
}
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
// Add access to cell
let cell = tableView.cellForRowAtIndexPath(indexPath) as? adminAlertsCell
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
println(cell!.banSwitch.on)
}
}
/////////////TableView - END
You should make the segue from the controller instead of directly from the detail disclosure button. Implement the method,
tableView:accessoryButtonTappedForRowWithIndexPath: and call performSegueWithIdentifier:sender: from inside that method. Pass the in-depth as the sender argument so you have access to that in prepareForSegue,
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
let indexPath = sender as NSIndexPath
if segue.identifier == "alertsDetail" {
let detailAlertViewController = segue.destinationViewController as DetailAlertViewController
let entryToPass = unpublishedPhotosObjectId[indexPath.row]
detailAlertViewController.entryId = entryToPass
}
}
Your issue is stated in the comments to your question, and there are several solutions. Perhaps the quickest since you only have one section, is to assign the indexPath.row to the tag of the cell. Then in your prepare for segue, the sender will be your cell. You can get the value out of it. So in your cellForRow method before your return your cell:
cell!.tag = indexPath.row
Then in your prepareForSegue:
let cell = sender as? adminAlertsCell
println(cell!.tag)
Here I am just printing it, but you can use it to pass the row.