I have this code, that I copied out of the default MasterDetail App, yet when I run the program, it stalls on the line with the declaration of the cell. And I have no clue why.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell
let object = objects[indexPath.row].title as String
cell.textLabel.text = object
return cell
}
You haven't register cell to use on table view
I hope you forgot to do this.
self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "Cell")
If you didn't register the cell for tableview, then it may return nil to cell.
Apple Says
let == Constant
var == variable values at any time
So using ? will consider as optional
you should have like this
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "Cell")
}
func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
var cell:UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
//OR
//var cell:UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("Cell") as? UITableViewCell
//Your stuff
return cell
}
According to This tutorial from Apple, you may want to check the Table View on the story board.
Look for the attribute inspector for the Table View and verify that the "content" field is set to "Dynamic Prototypes". Updating this fixed it for me.
Also, while you have the hood open, verify that there is a single table cell in the table view, and make sure that it's "Identifier" field is set to the value you had in the tableView.dequeueReusableCellWithIdentifier method call (in your case "cell")
Related
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellReuseIdentifier") as! CustomTableViewCell
let text = data[indexPath.row]
cell.label.text = text
return cell
}
above on is the code that I saw to follow.
my code is as below
I don't know why it getting nil value on
tableView.dequeueReusableCell(withIdentifier: "locCell")
my storyboard is as below
I added identifier like below(you can see it on bottom-right section of pic
You need to register the cell for reuse.
tableView.register(LocationTableCell.self, forCellReuseIdentifier: "locCell")
Or enter your reuse identifier in the storyboard by selecting your cell and then entering the reuse identifier in the properties to the right.
Simply because tableView.dequeueReusableCell(withIdentifier: "cell") by default is nil.
It is the same case for any optional when trying to print it out, example:
let optionalString: String? = ""
print(optionalString)
leads to get:
So, by declaring a constant as:
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")
since dequeueReusableCell(withIdentifier:) returns an optional UITableViewCell instance, the type of cell would be UITableViewCell? (optional UITableViewCell), that's why you are seeing this error.
How to get rid of it?
Assuming that you have set the cell right identifier for your cell:
Well, in case of having your custom cell, you could cast it as:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as? MyCustomCell else {
// something goes wrong
return UITableViewCell()
}
print(cell) // it would be fine for now
// ...
return cell
}
And if you don't have a custom cell, all you have to do is to remove the as? MyCustomCell down casting.
Replace
let cell = tableView.dequeueReusableCell(withIdentifier: "locCell")
With this code:
let cell = tableView.dequeueReusableCell(withIdentifier: "locCell", for: indexPath)
func tableView (_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "cellReuseIdentifier", for: indexPath) as! CustomTableViewCell
cell.label.text = data[indexPath.row].name
return cell
}
NOTE: In your storyboard set tableView Delegate, DataSource and set cell ID cellReuseIdentifier.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let row=animals[indexPath.row]
let cellIdentifier = "memoCell"
let cell=tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! MemoCellTableViewCell
print(cell)
print("mmmmmmm")
print(cell.subject)
cell.name?.text="aaa"
return cell
}
I'm unable to set value to label, because I have an error saying:
'unable to dequeue a cell with identifier Cell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard'
So, I added tableView.register(MemoCellTableViewCell.self, forCellReuseIdentifier: "memoCell"). Then, the problem is that I can't set a value to cell.name because cell.name is nil.
First check that your outlet to the cell.name is connected perfectly then you can try by updating your method in below manner,
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let row=animals[indexPath.row]
let cellIdentifier = "memoCell"
var cell=tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! MemoCellTableViewCell
if cell == nil {
cell = MemoCellTableViewCell(style: .default, reuseIdentifier: cellIdentifier)
}
print(cell)
print("mmmmmmm")
print(cell.subject)
cell.name?.text="aaa"
return cell
}
First of all remove this methods
tableView.register(MemoCellTableViewCell.self, forCellReuseIdentifier: "memoCell")
because your cell is in storyboard so you don't require this method, if you are using tableviewCell XIB then you can use this.
And in your MemoCellTableViewCell.swift file, create or connect all require outlet using storyboard and then try again. It should resolve issue, otherwise let me know.
I have read this question and think that I understand the difference between the two methods until I find a strange example:
Set table view cell's style be Basic, Identifier be Cell in Storyboard, code as below:
import UIKit
class TableViewController: UITableViewController {
var items: [String]!
override func viewDidLoad() {
super.viewDidLoad()
items = ["first", "second", "third"]
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// either works fine
let cell = tableView.dequeueReusableCellWithIdentifier("Cell")! // let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
cell.textLabel?.text = items[indexPath.row]
return cell
}
}
Very simple, but when I change the tableView:cellForRowAtIndexPath: method to 1, 2, 3, 4 cases respectively:
Case 1:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
cell.textLabel?.text = items[indexPath.row]
return cell
}
Case 2:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
cell = tableView.dequeueReusableCellWithIdentifier("Cell")!
cell.textLabel?.text = items[indexPath.row]
return cell
}
Case 3:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("Cell")!
cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
cell.textLabel?.text = items[indexPath.row]
return cell
}
Case 4:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("Cell")!
cell = tableView.dequeueReusableCellWithIdentifier("Cell")!
cell.textLabel?.text = items[indexPath.row]
return cell
}
Case 1, 2 (doesn't work):
Case 3, 4 (works fine):
How to explain? I think it really helps to understand these two methods from another perspective, any opinion is welcome.
In each case, you are dequeueing two cells for each row. In cases 1 and 2, you call the ("Cell", forIndexPath: indexPath) version first. In this case the table view ends up with two cells for each row, one completely overlapping and obscuring the other. You can see this in the view inspector since you can amend the angle of view to see behind:
(I amended the cellForRowAtIndexPath code like this:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("plainCell", forIndexPath: indexPath)
cell.textLabel!.text = "First cell for row \(indexPath.row)"
cell = tableView.dequeueReusableCellWithIdentifier("plainCell", forIndexPath: indexPath)
cell.textLabel!.text = "Second cell for row \(indexPath.row)"
print("Cell being returned is \(cell)")
return cell
}
to given different text labels to each cell.) In cases 3 and 4, where you call the ("Cell") version first, the table view has only one cell for each row.
Why the different behaviour? If you create a custom subclass of UITableViewCell and use that in your storyboard, you can then override various methods and add print() statements to see what's happening. In particular, awakeFromNib, didMoveToSuperView, and deinit. What transpires is that in cases 1 and 2, the first cell is created (awakeFromNib) and immediately added (didMoveToSuperView) to a superview, presumably the table view or one of its subviews. In cases 3 and 4, the first cell is created but is not added to a superview. Instead some time later, the cell is deallocated (deinit).
(Note that if the second cell is dequeued using the ("Cell", forIndexPath: indexPath) version, it too is added immediately to a superview. However, if the second cell is dequeued using the ("Cell") version, it is only added to a superview after the cellForRowAtIndexPath method has returned.)
So the key difference is that the ("Cell", forIndexPath: indexPath) version results in the cell being added immediately to the table view, before even the cellForRowAtIndexPath has completed. This is hinted at in the question/answer to which you refer, since it indicates that the dequeued cell will be correctly sized.
Once added to the superview, the first cell cannot be deallocated since there is still a strong reference to it from its superview. If the cells are dequeued with the ("Cell") version, they are not added to the superview, there is consequently no strong reference to them once the cell variable is reassigned, and they are consequently deallocated.
Hope all that makes sense.
dequeueReusableCellWithIdentifier: doesn't give you guarantees: cells could be nil, so you have to check if your cell is nil and handle it properly or your app will crash.
dequeueReusableCellWithIdentifier:forIndexPath:, on the other hand, does check this for you (it always return a cell).
For your particular case (Swift), this means you can safely force-unwrap the cell with dequeueReusableCellWithIdentifier:forIndexPath:, while you'll have to use the if let syntax with the second one.
Example codes (in Objective-C, I don't use Swift)
dequeueReusableCellWithIdentifier:forIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" atIndexPath:indexPath];
// Here we know the cell is not nil (....atIndexPath: ensures it)
cell.textLabel.text = items[indexPath.row];
return cell;
}
dequeueReusableCellWithIdentifier:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
// You asked for a cell, but you don't know if it is nil or not
// In Swift, here the cell should be a conditional
// First, check if the cell is nil
if ( cell == nil ) {
// Cell is nil. To avoid crashes, we instantiate an actual cell
// With Swift conditional should be something similar
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Cell"];
}
// Here you're sure the cell is not nil
// If condicional, you probably will write cell?.textLabel?.text = items[indexPath.row];
cell.textLabel.text = items[indexPath.row];
// Finally, you return the cell which you're 100% sure it's not nil
return cell;
}
I have a custom class for a table cell that is connected to a switch within the table cell (with an action) and I want to be able to to communicate to the TableViewController that the action happened as well as the path of the cell. The way that initially came to mind was if I could use some function in UITableViewCell to get the TableViewController of the table the cell is part of, as my custom class is (rather obviously) a subclass of UITableViewCell. Please tell me if I'm missing something.
To get a reference to the containing view controller, I add a weak property on the cell subclass.
#property (nonatomic, weak) UITableViewController *viewController;
You can assign this value in cellForRowAtIndexPath:
For accessing each cell in TableView
override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as YourTableViewCell
cell.mainTextLabel.text = self.venueService.mainCategoriesArray()[indexPath.row]
return cell
}
For getting selected cell in TableView
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let indexPath = tableView.indexPathForSelectedRow();
let currentCell = tableView.cellForRowAtIndexPath(indexPath) as UITableViewCell;
print(currentCell)
}
Reaching from TableViewCell to TableView
self.superview //// self is TableViewCell and its superview represents TableViewController
hope that helps
You should make the ViewController the target of the switch action.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("SwitchCell", forIndexPath: indexPath) as! SwitchTableViewCell
cell.onSwitch.addTarget(self, action: Selector("cellSwitchDidChange:"), forControlEvents: .ValueChanged)
cell.label.text = "This is a Switch"
return cell
}
func cellSwitchDidChange(sender: UISwitch) {
let origin = sender.convertPoint(CGPointZero, toView: tableView)
let indexPath = tableView.indexPathForRowAtPoint(origin)!
// do something
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as UITableViewCell
cell.textLabel!.text = self.funlists[indexPath.row]
return cell
}
cannot invoke 'dequeReusable...' with an argument of type '(String)'
on line var cell:UITableViewCell =
self.tableView.dequeueReusableCellWithIdentifier("cell") as
UITableViewCell
(sorry i'm extremely new to Xcode and swift)
when i click my UITableView in my storyboard, it is a table view cell with style Subtitle and identifier cell. (I may be doing this completely wrong, not sure)
I think I find the problem, try to take the word self of the dequeueReusableCellWithIdentifier call, like this:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:UITableViewCell = tableView.dequeueReusableCellWithIdentifier("cell") as UITableViewCell
cell.textLabel!.text = self.funlists[indexPath.row]
return cell
}
The reason been that you want to use the tableView coming from the function and not the tableView that belongs to the class.
Let me know if this solve your problem
Please define your cell identifier in storyboard.