I have two table views in same view controller. I am displaying authors in top table view. When I select an author the corresponding books of author should be displayed in bottom table view (list of books).
How can I handle tableView:didSelectRowAtIndexPath: for both these table views in my single view controller.
Can any one help me out how to achieve this task.
The function prototype for that UITableViewDelegate method is as follows:
func tableView(_ tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
That first argument is the UITableView in which the row was selected. So in your implementation you can simply do something like:
// Assuming you have two instances in your viewcontroller like this
var tableViewOne: UITableView!
var tableViewTwo: UITableView!
func tableView(_ tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if tableView == tableViewOne {
// User selected a row in tableviewOne
} else if tableView == tableViewTwo {
// User selected a row in tableviewTwo
}
}
If your code gets messy because you combine the logic for two UITableViewDelegates into one viewcontroller, then you can also create separate objects of course.
class FirstTableViewDelegate: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
// User selected a row in tableviewOne
}
}
class SecondTableViewDelegate: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
// User selected a row in tableviewTwo
}
}
class YourViewController: UITableViewController {
var firstDelegate: FirstTableViewDelegate!
var secondDelegate: SecondTableViewDelegate!
override func viewDidLoad() {
firstDelegate = FirstTableViewDelegate()
tableViewOne.delegate = firstDelegate
secondDelegate = SecondTableViewDelegate()
tableViewTwo.delegate = secondDelegate
}
}
Note that you must keep the delegates in instance variables in your own UIViewController. If you just create them locally or only assign them to the UITableView then they will be deallocated immediately since the delegate is a weak reference.
Related
So I have 2 Views that are shown inside a UIView, based on what is selected on the SegmentViewController. I create dummy data, returning 20 row of a custom cell. This works great.
Everything is fine, till I interact with the TableView.
Bellow is my code:
GoalsViewController.swift
import UIKit
class GoalsViewController: UIViewController {
#IBOutlet weak var goalsTableView: UITableView!
let goalCellIdentifier = "goalCell"
override func viewDidLoad() {
super.viewDidLoad()
goalsTableView.delegate = self
goalsTableView.dataSource = self
goalsTableView.register(UINib(nibName: "GoalsViewCell", bundle: nil), forCellReuseIdentifier: goalCellIdentifier)
goalsTableView.reloadData()
}
}
extension GoalsViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 20
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = goalsTableView.dequeueReusableCell(withIdentifier: goalCellIdentifier, for: indexPath) as! GoalsViewCell
cell.goalTitle.text = "aaaaa"
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("Selected \(indexPath.row)")
}
}
After any of the empty rows is selected, the didSelectRowAt is not called, so the cells are not there at all. I tried to find a solution, but I was only to find issues about empty lists, before being populated.
What could be the reason for the empty tableview?
I might be wrong here but one thing that I've noticed is that you are not implementing a function which sets the height of each cell.
// Specify the height of your cells
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100 // Or your given cell height.
}
So here is my theory: If you are using constraints something is missing and your cell's height can't be identified by constraints alone or you are not using constraints at all thus you must use heightForRowAt function to specify each cell's height.
I will explain what was the issue for people who probably did not know (like me).
So my UITableView is inside a UIView that changes based on what the user is selecting. In total I had 2 different Views that where switching. The reason that it was emptying it was because my parent ViewController, could not access the delegate for UITableView. To fix that, after adding a subview to the UIView, you need also to move the ViewController to the parent controller. In code it goes like this.
// Empty array of UIViewControllers
var views: [UIViewController]!
// Add the UIViewControllers to the array
views = [UIViewController()]
views.append(EventsViewController())
views.append(GoalsViewController())
for view in views {
// Needed to adjust the size of Subview to size of View
view.view.frame = containerView.bounds
// Add the subviews
containerView.addSubview(view.view)
}
// Bring the view in position 1 to the front of the UIView
containerView.bringSubviewToFront(views[1].view)
// Add Views[1] UIViewController as a child to the parent controller
self.addChild(views[1])
views[1].didMove(toParent: self)
// After done with everything with the UIViewController remove it
views[1].removeFromParent()
addChild Apple.com
didMove Apple.com
This question seems to have been asked many times on StackOverflow, but after searching for hours, I still haven't found the solution to my issue.
I have a table view in SecondViewController.swift and I did make it delegate and dataSource SecondViewController.swift, but the didSelectRowAt method is still not fired when a table cell is clicked. I am trying to apply a segue to another view controller, but the didSelectRowAt method isn't working itself.
I have selected Single Selection in my storyboard in the table view properties, and I have made sure that user interaction is enabled for the table view and the prototype cell.
For the SecondViewController.swift below, please assume that allEvents are populated into the allEvents array. I was able to successfully populate them and display them in the table. The only problem is clicking on the row to perform a segue. Also, please ignore the awkward indentation below.
SecondViewController.swift
import UIKit
class SecondViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet var tableView: UITableView!
var allEvents: Array<JSON> = []
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
//self.tabBarItem.image = myTabImage
self.tabBarController?.tabBar.isTranslucent = false
self.tabBarController?.tabBar.barTintColor = UIColor(red:0.25, green:0.25, blue:0.25, alpha:1.0)
self.tabBarController?.tabBar.tintColor = UIColor.white
//ASSUME ALL EVENTS ARE POPULATED INTO allEvents
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: Tableview Datasource
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return allEvents.count
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 150
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("SEGUE")
self.performSegue(withIdentifier: "oneSegue", sender: allEvents[indexPath.row])
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "eventViewTableCell", for: indexPath) as! EventViewTableCell
return cell
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// let detailController = segue.destination as! EventDetailsController
}
}
I have been on this for hours and still have not found out the problem. I would greatly appreciate any help!
You may want to check your tableview again, do choose single selection.
I'm assuming you are selecting No Selection as the picture bellow
Hope it helps.
I had the same problem on a Controller within a TabBarController.
What fixed it for me was this:
Disable the User Interaction for the Content view
My guess is that it was becoming the responder so it removed the event from the TableView
Note: Since the viewDidLoad assignments to delegate and dataSource became redundant after setting it up in the StoryBoard, I removed them, although leaving them does no harm.
If you do nothing else than calling the segue in didSelectRowAt remove the entire method and connect the segue in Interface Builder to the table view cell (rather than to the controller). The sender parameter will contain the reference to the selected table view cell.
I suspect when you collected and populated your allEvents array did you collect any touch events in there ?
or have you used UIGestureRecognizer in this or it's super view ?
If you have intercepted any touch event to populate your array or any other purpose comment it out and test it with a simple preloaded array like :
let dataSource = ["1", "2", "3", "4"]
let me know if this is not your case so I can find another solution.
I have a ViewController that calls (clicking on a button) another View using this function
#IBAction func btnSeeContact(sender: AnyObject) {
self.performSegueWithIdentifier("segueSeeContact", sender: self)
}
and my prototype cell is "linked" to a custom View Controller named ContactsTableViewCell that I have created and it implements:
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! ContactsTableViewCell
cell.txtName.text = "test"
cell.txtPhone.text = "1234567890"
return cell
}
When I run the project, the button calls the table, but there is no Cell on it, and I put a breakpoint on those tableView functions and they are not being reached.
What am I missing here that those functions are never being called?
I am adding a new answer since my previous answer was up voted, so I don't want to make massive edits that one, and is still a valid way to fix your issue.
The issue is you have your custom classes confused. In your screen shot you can see that the the Table View Controller is not set to a custom class, it just says Table View Controller. That is the object that needs to get a custom implementation of the UITableViewController class.
Instead you seem to be setting the cell's class to a custom class, and implementing the delegate methods there. You still need a custom class for the table view cell, but it should be a custom class of UITableViewCell.
So your cell class should look something like this:
import UIKit
class YourCustomTableViewCell: UITableViewCell {
#IBOutlet weak var yourLabel1: UILabel!
#IBOutlet weak var yourLabel2: UILabel!
}
You will be given an instance of this cell to configure in cellForIndexPath.
So your Table view controller class should be set to a class that looks like below. The YourTableViewController is were you want to implement all the delegate methods.
Note: if you are using a UITableViewController dragged out from the storyboard, it will already have the tableView, and delegate / data source stuff already wired up for you. You will also notice that you are overriding the delegate methods as the UITableViewController class has default implementations of these. If you are just using a normal view controller, then see my previous answer for more details on how to set that up.
import UIKit
class YourTableViewController: UITableViewController {
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return 1
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("reuseIdentifier", forIndexPath: indexPath)
if let cell = cell as? YourCustomTableViewCell {
cell.yourLabel1.text = "some text"
cell.yourLabel2.text = "some other text"
}
return cell
}
}
As others have commented, you really need to provide a little more context.
Here are a few things that might be going wrong, providing more context would confirm or deny this guesses.
First you don't show the numberOfSectionsInTableView method.
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return 0
}
I think you would need to provide a value other than 0
Secondly, since I don't see override in front of what I am sure you are intending to be UITableViewDelegate methods function calls, that means your view controller is not a UITableViewController. This makes me wonder if you defined this view controller as conforming to the UITableViewDelegate protocol and if you set the table view outlet delegate to self. (or even wired up the UITableView to an outlet)
If you use a plain UIViewController to host a table view you need to do the following:
Wire up your UITableView to an outlet in your view controller
Declare the view controller as conforming to the UITableViewDeleagate (and maybe UITableViewDataSource) protocol
set the table view's outlet delegate (and maybe dataSource) properties to self (the view controller implementing the protocols)
Implement the required methods
So something like this:
class MyTableViewController: UIViewController, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return 1
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("your PrototypeCell", forIndexPath: indexPath)
// Configure the cell...
return cell
}
}
Numerous tutorials I've been through say the only code I need to display the array I want it to is:
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var chatListTableView: UITableView!
var friends = ["Anthony", "Antonio", "Andy"]
override func viewDidLoad() {
super.viewDidLoad()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return friends.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell : UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "ChatListCell")
cell.textLabel.text = self.friends[indexPath.row]
return cell
}
}
However, when I run the app, the tableView is still blank. What am I doing wrong? I feel that I am missing something.
All I want to do is display the array in the tableView.
Check if the ViewController is the datasource and delegate of the tableview
As Aci says in his answer, you have to set the data source and delegate of the table view to your view controller. The easiest way to do that is in Interface Builder.
New to IOS development and am having trouble with handling cell selection on a table. Whenever I select, the method is not getting called below - any idea why?
My project structure is:
View Controller -> View -> Table View
The below code demonstrates the method calls. The others get called no problem! I know touch is working as pull down successfully refreshes and on clicking a cell it does become highlighted.
import UIKit
class ViewController: UIViewController, UITableViewDelegate
{
let blah = ["blah1"]
//How many sections are in the table?
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
//How many rows? (returns and int)
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return blah.count
}
//table contents for each cell?
//Each time this is called it'll return the next row and thus build a table...
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
print("Populating each cell of table view!\n")
tableView.rowHeight = 80.0
var cell = UITableViewCell()
var(a) = blah[indexPath.row]
var image : UIImage = UIImage(named: a)!
cell.imageView.image = image
return cell
}
//Code Cell Selected
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath){
println("You selected cell #\(indexPath.row)!")
}
func tableView(tableView: UITableViewDelegate, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
print("wananananaanan" )
println("You deselected cell #\(indexPath.row)!")
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Everybody is mentioning to set dataSource and delegate of the tableView.
But after setting also not working fine then sometimes it may happen because of none or disable selection of table view.
To enable it
Go to storyboard -> Select tableView -> click on the attribute inspector ->go to selector -> Select selection as single selection (or multiple selection according to the requirements.)
Please find attached screenshot for your suitability.
You have to set an #IBOutlet to the tableView in you ViewController and set as it's delegate and dataSource to you can see the data an respond to changes in the tableView.
Something like this :
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.delegate = self
self.tableView.dataSource = self
}
And implements the UITableViewDataSource protocol too.
Or you can too in the Interface Builder set the ViewController as it's delegate and dataSource (more easy to do I think) and avoid to set manually in code like above. Is up to you.
I hope this help you.
SWIFT 3
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Do here
}
Use the above delegate method in swift 3
Couple of checks that can help you:-
myTableView.allowsSelection = true
myTableView.delegate = self
Make sure you written didSelectRowAt correctly:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
If you are using UIButton on UITableViewCell then it overlaps cell so check Solution here
I faced the same issue when compared two identical code examples where one was working well and the other was not calling didSelectRowAtIndexPath
Take a look at two possible ways to solve the issue:
1) In the code itself:
#IBOutlet weak var table: UITableView!
override func viewDidLoad() {
table.delegate = self
table.dataSource = self
//data source might be already set if you see contents of the cells
//the main trick is to set delegate
}
2) Using Storyboard or Document Outline (which was the problem in my case cause storyboard changes are not visible in .swift controller classes.
Open Document Outline and Control + Press your TableView
you will see two outlets called "delegate" and "dataSource"
drag them 1 by 1 to the containing ViewController (right onto the yellow circle)
That's it!
Another caveat is tap gesture recognizers. It's a common use case to use tap gesture recognizer to handle different logic within your view controllers with table views, whether that's exiting touch control or first responders.
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(viewTapped))
view.addGestureRecognizer(tapGesture)
E.G. This line of code handles dismissing a date picker in my application and prevents my tableview from calling didSelectRow delegate method
You have to use this: First take a look what are you extending and then use the tableView method.
class YourViewController : UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var mUITableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// We need to tell to UITableView that we will add the data by ourselves
self.mUITableView.delegate = self
self.mUITableView.dataSource = self
// Register the UITableViewCell class with the tableView
self.mUITableView?.registerClass(UITableViewCell.self, forCellReuseIdentifier: self.cellIdentifier)
// Setup table data
getEvents()
self.mUITableView.allowsSelection = true
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tableData.count
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
// here to create you cell view
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("You selected cell #\(indexPath.row)!")
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: "subtitleCell")
cell.accessoryType = UITableViewCellAccessoryType.DisclosureIndicator
cell.textLabel?.text = "\(tableData[indexPath.row].name) - (\(tableData[indexPath.row].eventStateId))"
cell.detailTextLabel?.text = tableData[indexPath.row].lastUpdate
return cell
}
}
Another reason you may write this function which allowed to click under condition
func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool {
if(indexPath.section == 1){
return true
}
return false
}
Another caveat which took me ages to figure out is to make sure that all three of your Table View, your Cell and your Content View all have User Interaction Enabled. Then in Swift 4, at least, you can use:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
if you are editing your tableView:
tableView.allowsSelectionDuringEditing = true
Another thing to check is the access level for your class & method:
I had a Swift UIViewController class marked #objc public, to make it visible to my objective-c code.
In this context you must add public access to this function, or it will not be called.
public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
Nothing happened till I've cleaned and built again. :)