UITableViewDataSource doesn't get invoked - swift

I am trying to learn swift which is also my first programming language. I am trying learn by creating an app with table view in it. I have added a table view and table cell(myCell as identifier). Table cell in turn has labels and text field. Also the background color of the table cell is yellow.
The view controller which encapsulates all this is hooked to class showDetailViewController as show below.
class showDetailViewController:UIViewController, UITableViewDataSource, UITableViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//UITableViewDataSource
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell: TaskCell = tableView.dequeueReusableCellWithIdentifier("myCell") as TaskCell
return cell
}
//UITableViewDelegate
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
}
}
When I run my application I expect to see my cell replicated 5 times (as number of cells in section returns 5) in table view but upon debug I realized that the no tableview delegate class get invoked and hence the issue.
Can anyone please help me with this issue?
Thanks,
Dev

Add the tableView as an IBOutlet.Then you should set tableView's datasource to viewController. self.tableView.dataSource = self. You can also set tableView's dataSource in Interface bulider.

Related

How to modify a custom cell inside of DidSelectRowAtIndexPath

A custom cell class has the override func layoutSubviews() where the detailTextLabel of each cell is given the title "Jim". Upon clicking on DidSelectRowAtIndexPath, is there a way to change the detail text of the cell permanently(to stop the cell from constantly making the detail Jim), to let's say "Bob"?.
//This function is in the custom cell UserCell
class CustomCell: UITableViewCell {
override func layoutSubviews() {
super.layoutSubviews()
detailTextLabel?.text = "Jim"
}
///........
}
//In the viewController with the tableView outlet
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
//.....
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(cellId, forIndexPath: indexPath) as! CustomCell
//......
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
/* Code I need to convert the detailTextLabel.text to equal "Bob" upon clicking on certain cell */
}
The cell itself shouldn't be used to hold state for any data, but only to display it. Create a mutable array property on the controller to hold the underlaying data (strings). Set the new cells' text properties by reading this array, and in tableView:didSelectRowAtIndexPath: change the value in the array at the index for "Bob" to "Jim". Whenever the tableView reloads it will now read the updated value from the dataSource.
In addition to the UITableViewDelegate protocol also study the UITableViewDataSource protocol. By default the UITableViewController class conforms to both of these protocols and is assigned as each on its .tableView property (if you introspect its self.tableView.delegate and self.tableView.datasource values you will receive back the original UITableViewController). If you manually created your own tableview controller class that inherits from UIViewController, then you will need to assign both of these properties on the tableView in order for it to function properly.
Very simple, do it like this:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
detailTextLabel?.text = "Bob"
}

Swift: How to enter edit mode for table?

I have a RootViewController that embeds a container that contains a table:
I'd like the garbage can icon on the top left of the RootViewController to enable editing mode for the embedded table view. I would like them to show up as checkboxes so that I can select multiple rows at once and then press "Delete" to delete all of the selected ones.
How would I do this?
Hopefully your class already conforms to UITableViewDelegate like so:
class MyViewController: UIViewController, UITableViewDelegate
In viewDidLoad(), you would need to have:
myTable.delegate = self
Then you can hook up the trash can icon to an IBAction that sets the table to editing mode:
#IBAction func myTableSetEditing(sender: AnyObject) {
myTable.setEditing(true, animated: true)
}
Then, as we see in an answer here: Select multiple rows in tableview and tick the selected ones, in viewDidLoad() put:
self.tableView.allowsMultipleSelection = true
and to get your checkmark, implement:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.cellForRowAtIndexPath(indexPath)?.accessoryType = UITableViewCellAccessoryType.Checkmark
}
override func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
tableView.cellForRowAtIndexPath(indexPath)?.accessoryType = UITableViewCellAccessoryType.None
}

Why can't I select TableView cells?

This is the flow of my app:
First, the TableView is set to hidden. There is a UITextField in the center of the screen. When the user types something and hits Go, this code is run:
self.view.layoutIfNeeded()
UIView.animateWithDuration(0.5, animations: {
self.textFieldConstraint.constant = -230
self.tableView.hidden = false
self.goButton.hidden = true
self.view.layoutIfNeeded()
}, completion: nil)
At this point, the tableview is populated. When a row is selected, I need to manipulate the data that is populating it.
However, absolutely nothing happens when I tap a cell.
What am I doing wrong?
My TableView code is here:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: SearchResultsTableViewCell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! SearchResultsTableViewCell
cell.label.text = searchResultsNames[indexPath.row]
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return searchResultsUrls.count
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("HELLO")
}
And, I have set the dataSource and delegate properly.
I also want to clarify that the tableView populates and scrolls properly; it just won't do anything when I tap a cell.
Update:
I've discovered that for some reason, I can select the cells when I press and hold them. It is not what I want, so does anybody know how to fix this?
I have just used your code to create a simple table, selection is working fine and logging out HELLO as expected. Can you check the values of Selection in the attributes inspector? Here is mine, which has Selection set to Single Selection.
And here is the code I used for my simple table
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
var searchResults = [String]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
searchResults.append("Testing 1")
searchResults.append("Testing 2")
searchResults.append("Testing 3")
searchResults.append("Testing 4")
searchResults.append("Testing 5")
tableView.dataSource = self
tableView.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("tableCell", forIndexPath: indexPath)
cell.textLabel?.text = searchResults[indexPath.row]
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return searchResults.count
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("HELLO")
}
}
I also tried hiding and showing the tableView which made no difference on selection.
EDIT & SOLUTION:
In the comments below, we discovered that the issue is related to a tapGestureRecogniser on the view, This was identified by the op only being able to make a selection by holding a tap on the cell. The gesture has to fail before the selection can be made, the op managed to solve the problem by referring to this other SO Answer
if you use "tap" gesture, you can't select table cell. (but, if you click and drag to right a cell, you can select it.)
Check gesture first.
And if your code has self.tableView.allowsSelection = false, replace false to true or delete this line.
My problem has been caused by the tap gesture recognizer on the view controller itself (I had BaseTableViewController I was extending from). Probably it was interfering with the gesture recognizers of UITableView.
In your viewDidLoad, or wherever you set up your view, make certain that your table view even allows selections. That can be controlled with the allowSelection property.
Something like:
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.allowsSelection = true
}
If you have a gesture recognizer, just type gestureRecognizer.cancelsTouchesInView = false
For me I was implementing another did select row method, so I erased it and typed "didSelect..." and selected the first one in the suggested methods, which is this for swift 3:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("Row #: \(indexPath)")
}
Try disabling and then enabling the user interaction Enabled property in the attribute inspector of your tableView
Looks something like this
I met the same problem as you and solved it by removing the below code
self.view.layoutIfNeeded()
you can try it.

UITableView, does not conform to protocol - how to declare several?

I understand that I need to implement required methods in the controller and establish a relationship between view and controller.. but where do the identifier "tableView" in
func tableView(mintabell: UITableView, numberOfRowsInSection section: Int) -> Int
{
return items.count
}
come from and what if I want several tableviews on same view? How to declare them separately?
These are the protocols that you need to declare for a TableView
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
return createCellAndReturnItHere
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return numberOfCellsInSection
}
In terms of having more than one tableview in one ViewController.. If you have an outlet for each tableview then you can check which one you need inside the protocol functions:
#IBOutlet weak var tableViewOne: UITableView!
#IBOutlet weak var tableViewTwo: UITableView!
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if tableView == self.tableViewOne {
return createCellForTableViewOneAndReturnItHere
} else {
return createCellForTableViewOneAndReturnItHere
}
}
Think of it like a box. Every tableView that is subscribed to the protocol grabs in that box leaves its fingerprint and takes what it gets. So if you have multiple tableViews in one controller you can distinguish them by checking for equality.
Example:
class MyViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
let firstTableView = UITableView()
let secondTableView = UITableView()
viewDidLoad() {
firstTableView.delegate = self
secondTableView.delegate = self
firstTableView.dataSource = self
secondTableView.dataSource = self
}
// ... some othe methods...
func tableView(mintabell: UITableView, numberOfRowsInSection section: Int) -> Int
{
if tableView == firstTableView {
return 10
}
if tableView == secondTableView {
return 20
}
return 0
}
}
Delegates are implemented by the developer and called by iOS not the developer. The tableView is passed by iOS and points to the particular tableview.
If you have several tableviews with the same delegate you can compare the passed tableview parameter to the tableviews you have implemented to determine which one.
Alternatively, create a tableView delegate and datasource per tableView. This will eliminate testing which tableView removing a lot of conditional logic from the code.
First of all, you don't necessarily always have to implement a protocol for every view. Only when you have a view which has delegate that needs implementing, you will have to conform that protocol responsible for that delegate implementation.
So for tableView, first you drag a UITableView in your Controller from the Object Library and then a UITableViewCell under the Table View.
Now go to the ViewController.swift file and add
#IBOutlet var myFirstTableView: UITableView!
right after the line.
class ViewController: UIViewController
P.S: If you have more than one table then you could just declare the extra table's here., like-
#IBOutlet var mySecondTableView: UITableView!
Now, let's assume, you have one table. Now, you need to add the list of protocols. So, just add UITableViewDelegate, UITableViewDataSource with
class ViewController: UIViewController
appended by comma.
Adding this should give you an error but that's okay. This is because you have not added the required method listed under that UITableViewDatasource protocol.
So, just add those required methods and implement it accordingly.
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3;
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("myCell") as! UITableViewCell
cell.textLabel?.text = "test"
return cell
}
Here, I said, I will have 3 cells whose identifier is "myCell" and the cell's textLabel will have a text, "test".
Now, we forgot a very important step and that is to assign the cell identifier to our TableView Cell. So, go to the storyboard and select the TableView Cell and insert "myCell" as an identifier for your cell.
If you had more than one table, then you would check for which TableView, you are loading your data. So, you could assign a unique tag to each table explicitly(you can do that either from storyboard or from code), and based on that tag, you would implement your methods. Let's say you have 3 tables and the assigned tag is 1,2 and 3. So, you could do something like,
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView.tag == 1{
return 3
}
else if tableView.tag == 2{
return 4
}
else{
return 1
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as! UITableViewCell
if tableView.tag == 1{
cell.textLabel?.text = "test1"
}
else if tableView.tag == 2{
cell.textLabel?.text = "test2"
}
else{
cell.textLabel?.text = "test"
}
return cell
}

Implementing UITableViewController with Storyboard

I've just started my first iOS app (having just finished an OSX app). It's currently got 3 tabs where the first contains a UIView that shows a Google map and the other 2 are UITableViewControllers and I'm trying to set them up.
My storyboard looks like this: https://imgur.com/uNMux7h,ivheZFi
and the UITableViewController class like this: https://imgur.com/uNMux7h,ivheZFi#1
I have connected the class to my delegate and data properties of the UIViewController but I can't connect an outlet, am I off to a poor start here? Does anyone have a tutorial for starting out with the UITableViewController class with Swift?
A standard class for UITableViewController looks something like the following code. Just make sure in interface builder you set the class of the table view controller to SecondViewController and make sure your cell identifier in interface builder matches the cell identifier from your class.
import UIKit
import Foundation
class SecondViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("CellID", forIndexPath: indexPath) as UITableViewCell
cell.textLabel?.text = "some text"
return cell
}
}