How to reuse cells in a UITableView? - ios

I'm running into some problems today in my application. I'm using a UITableView to present different cells some contain a specific button, some display a game your currently in, some are just for spacing between cells.
However im running into some problems when I do tableView.reloadData().
When I click on 'new game' a new game cell should be added, but instead of that the last cells are moved down (like expected, because something comes in between), however the upper cells that should change, they don't. I expect this to be because of reusing (or "caching"). Maybe someone can help me out on how to fix this.
Here is an image of what is happening explanation
Now I have different cells, all with their own Identifiers for example: "Emptycell", "Gamecell", "startnewgameBtnCell". I do this because I create each cell in storybuilder.
here is my code for cellForRowAtIndexPath
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell()
let identifier = cell_identifiers[indexPath.row][0] as! String
if ((cell_identifiers[indexPath.row][1] as! String) == "MYTURN" ) {
var cell = tableView.dequeueReusableCellWithIdentifier(identifier, forIndexPath: indexPath) as! GameViewCell
let match = self.myTurnMatches[indexPath.row - 4]
setupGameViewCellObject(match)
}
if ((cell_identifiers[indexPath.row][1] as! String) == "NOT" ) {
var cell = tableView.dequeueReusableCellWithIdentifier(identifier, forIndexPath: indexPath) as! FirstMenuTableViewCell
}
return cell
In this code I check if its a gamecell or not, emptycells or cells that contain a label or button are all FirstMenuTableViewCell. Only GameCell have their own class GameViewCell.
I have also double checked to see if my identifiers are build up correctly and they are.
Maybe someone can explain me exactly whats happening and what might be the correct approach to solve the situation I'm in, I think I may not fully understand how to use UITableViewCell with custom cells.
Thanks for reading

You create new variables called cell in the if statements. You are hiding the outer cell.
Use one cell variable.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell: UITableViewCell
let identifier = cell_identifiers[indexPath.row][0] as! String
if ((cell_identifiers[indexPath.row][1] as! String) == "MYTURN" ) {
cell = tableView.dequeueReusableCellWithIdentifier(identifier, forIndexPath: indexPath) as! GameViewCell
let match = self.myTurnMatches[indexPath.row - 4]
setupGameViewCellObject(match)
}
if ((cell_identifiers[indexPath.row][1] as! String) == "NOT" ) {
cell = tableView.dequeueReusableCellWithIdentifier(identifier, forIndexPath: indexPath) as! FirstMenuTableViewCell
}
return cell
}

Try update your code:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell()
let identifier = cell_identifiers[indexPath.row][0] as! String
if ((cell_identifiers[indexPath.row][1] as! String) == "MYTURN" ) {
var cell = tableView.dequeueReusableCellWithIdentifier(identifier, forIndexPath: indexPath) as! GameViewCell
let match = self.myTurnMatches[indexPath.row - 4]
setupGameViewCellObject(match)
return cell
}
if ((cell_identifiers[indexPath.row][1] as! String) == "NOT" ) {
var cell = tableView.dequeueReusableCellWithIdentifier(identifier, forIndexPath: indexPath) as! FirstMenuTableViewCell
return cell
}
return cell
if me, i coded:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let identifier = cell_identifiers[indexPath.row][0] as! String
if ((cell_identifiers[indexPath.row][1] as! String) == "MYTURN" ) {
let cell = tableView.dequeueReusableCellWithIdentifier(identifier, forIndexPath: indexPath) as! GameViewCell
let match = self.myTurnMatches[indexPath.row - 4]
setupGameViewCellObject(match)
return cell
}else { // if you sure just have 2 cell type
let cell = tableView.dequeueReusableCellWithIdentifier(identifier, forIndexPath: indexPath) as! FirstMenuTableViewCell
return cell
}
}
But i think you should use section in tableview,it is better. So, you have 3 section, and 2 headerofsection in there. the first section have 1 row, the second have self.myTurnMatches.count row.... :
override func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
if section == 0 || section == 1{
return 44
}else{
return 0
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.section == 1{
let cell = tableView.dequeueReusableCellWithIdentifier("GameViewCell 's identifier", forIndexPath: indexPath) as! GameViewCell
let match = self.myTurnMatches[indexPath.row - 4]
setupGameViewCellObject(match)
return cell
}else{
let cell = tableView.dequeueReusableCellWithIdentifier("FirstMenuTableViewCell 's identifier", forIndexPath: indexPath) as! FirstMenuTableViewCell
return cell
}
}

Related

switching between different classes for UITableViewCell

I have two data sources, and two different classes for custom cells in my table.
I want by pressing one button to switch between sources and classes and update my UITableView accordingly.
Unfortunately It works only one time I switch from one set to another. It doesn't return back.
Hope my code will help to explain what I mean:
var displayMode : Int = 1
#objc func tappedButton(_ sender: UIButton?) {
if displayMode == 1 {
displayMode = 2
myTable.reloadData()
} else {
displayMode = 1
myTable.reloadData()
}
}
override func tableView(tableView: UITableView,
cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if displayMode == 1 {
let cell = tableView.dequeueReusableCellWithIdentifier(cellId,
forIndexPath: indexPath) as! Class1
cell.taskTitle.text = source1.text
return cell
}
else {
let cell = tableView.dequeueReusableCellWithIdentifier(cellId,
forIndexPath: indexPath) as! Class2
cell.taskTitle.text = source2.text
return cell
}
}
Should I delete table cells before changing mode?
You use the same cellID in
let cell = tableView.dequeueReusableCellWithIdentifier(cellId,
forIndexPath: indexPath) as! Class1
and
let cell = tableView.dequeueReusableCellWithIdentifier(cellId,
forIndexPath: indexPath) as! Class2
Should be two different cells for 2 different classes (2 different IDS)
1) You need to create 2 separate classes for cells:
class FirstCellClass: UITableViewCell {}
class SecondCellClass: UITableViewCell {}
2) Then register the cells(or add cells in Storyboard):
tableView.register(FirstCellClass.self, forCellReuseIdentifier: String(describing: FirstCellClass.self))
tableView.register(SecondCellClass.self, forCellReuseIdentifier: String(describing: SecondCellClass.self))
3) Check display mode and return specific cell cellForRowAtIndexPath and items count in numberOfRowsInSection:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch displayMode {
case .first:
return firstDataSource.count
case .second:
return secondDataSource.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch displayMode {
case .first:
let cell = tableView.dequeueReusableCell(
withIdentifier: String(describing: FirstCellClass.self),
for: indexPath
) as! FirstCellClass
configureFirstCell(cell)
return cell
case .second:
let cell = tableView.dequeueReusableCell(
withIdentifier: String(describing: SecondCellClass.self),
for: indexPath
) as! SecondCellClass
configureSecondCell(cell)
return cell
}
}

TableViewCell not updating properly swift 2.2

In my app I created two custom tableview cells.
Problem I am facing now the second tableview cell update with last element of the array only.
In cellForRowAtIndexpath array elements are displaying fine.
Consider [ "Value1", "Value2" ] is my array. In tableView only value2 is displaying in two cells.
var title = ["value1","value2"]
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let row = indexPath.row
let x = Id[indexPath.row]
if x == 0{
let cell1 = tableView.dequeueReusableCellWithIdentifier("Cell1", forIndexPath: indexPath) as! MyCell1
return cell1
}
else{
let cell2 = tableView.dequeueReusableCellWithIdentifier("Cell2", forIndexPath: indexPath) as! MyCell2
for index in 0..<myArray.count{
cell2.titleButton.setTitle(title[index],forState:UIControlState.Normal)
}
return cell2
}
}
I am stuck here, your help will be appreciated.
Following is the solution, reason it was going out of range was because value incremented when cell were dequed as cellforrowAtIndexPath was called every time we scrolled down(since some cells were not visible and these cells were dequed when we scrolled down):-
var name = ["HouseBolo","HouseBolo1","HouseBolo2","HouseBolo3"]
var propertyVal:Int = 0
var projectVal:Int = 0
var type = ["Apartment","Villa","Home","Flat","Plot"]
var arrangedData = [String]()
var flatId = [0,1,2,0,0]
override func viewDidLoad() {
super.viewDidLoad()
// I want the Expected Output in Tableview Cell is
// 1. Apartment 2. HouseBolo 3. HouseBolo1 4. Villa 5. Home
for item in flatId {
if item == 0 {
arrangedData.append(type[propertyVal])
propertyVal+=1
}
else {
arrangedData.append(name[projectVal])
projectVal+=1
}
}
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrangedData.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let flatDetails = flatId[indexPath.row]
// For Property Cell
if flatDetails == 0{
let cell = tableView.dequeueReusableCellWithIdentifier("Cell1", forIndexPath: indexPath) as? PropertyCell
if(cell != nil) {
cell!.pType.text = arrangedData[indexPath.row]
}
return cell!
}
// For Project Cell
else {
let cellan = tableView.dequeueReusableCellWithIdentifier("Cell2", forIndexPath: indexPath) as? ProjectCell
if(cellan != nil) {
cellan!.projectName.setTitle(arrangedData[indexPath.row], forState: UIControlState.Normal)
}
return cellan!
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
You need to use :
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! searchCell
in :
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {}
block.
searchCell : is a class of type : UITableViewCell
After that, go in Storyboard and change the identifier of your cell with : "cell"
In the code...
let cell2 = tableView.dequeueReusableCellWithIdentifier("Cell2", forIndexPath: indexPath) as! MyCell2
for index in 0..<myArray.count{
cell2.titleButton.setTitle(title[index],forState:UIControlState.Normal)
}
return cell2
you are iterating over 'myArray' and assigning the value to 'cell2.titleButton'. Cell 2 will always have the last value assigned to it's title. It's assigning it to 'value1', then reassigning it to 'value2'. Looping through the array seems to be the issue (assuming the cells are displaying - just always showing the title from the last item in the array.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
}
You have to add your custom tableview cell class name in the place of UITableViewCell
Something like this -
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> **custom tableview cell class name**
{
}
And also in Storyboard ,change the identifier of your cell with : "cell1" and "cell2"

Using multiple UITableview in Single ViewController with Custom TableViewCell

How can I return a TableViewcell in cellForRowAtIndexPath delegate method , if I m using multiple UITableView in a Single ViewController, One TableView has Custom UITableViewCell and the other has default UITableViewCell . My problem is I m not able to cast the UITableViewCell to Custom TableViewCell type .
Code used as follows ,
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:UITableViewCell?
if tableView == self.tvCity {
var cell:UITableViewCell?
cell = tableView.dequeueReusableCellWithIdentifier(cityCellIdentifier, forIndexPath: indexPath) as UITableViewCell
let row = indexPath.row
cell!.textLabel?.text = self.cityList[row].cityEn
}
if tableView == self.tvBranchByCity {
var cell:BranchNearMeTableViewCell?
cell = (tableView.dequeueReusableCellWithIdentifier(branchCellIdentifier, forIndexPath: indexPath) as! BranchNearMeTableViewCell)
let row = indexPath.row
cell.branchName = self.branchList[row].name// here cell.branchName is not accessible.
}
return cell!
}
Please advise . Thanks in advance.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if tableView == self.tvCity {
var cell:UITableViewCell?
cell = tableView.dequeueReusableCellWithIdentifier(cityCellIdentifier, forIndexPath: indexPath) as UITableViewCell
let row = indexPath.row
cell!.textLabel?.text = self.cityList[row].cityEn
return cell!
}
if tableView == self.tvBranchByCity {
var cell:BranchNearMeTableViewCell?
cell = (tableView.dequeueReusableCellWithIdentifier(branchCellIdentifier, forIndexPath: indexPath) as! BranchNearMeTableViewCell)
let row = indexPath.row
cell.branchName = self.branchList[row].name as! String // here cell.branchName is not accessible.
return cell!
}
}
Differentiate tables with tag
see example :
if tableView.tag == 2000
{
identifier = "First Table"
let cell = tableView.dequeueReusableCellWithIdentifier(identifier, forIndexPath: indexPath)
return cell
}
else
{
identifier = "Second Table"
let cell = tableView.dequeueReusableCellWithIdentifier(identifier, forIndexPath: indexPath)
return cell
}

Need some cells static in Dynamic Prototype table

Hello In my TableViewController I need first three cells static and then dynamic cells. I have done some research and came up that I can do static cells in dynamic table Prototype but not vice versa.
So I drag a tableViewController and choose dynamic prototype and added 4 Prototype Cells(3-static cells. and 1 dynamic) and give each four of them different identifiers and also added the class TableViewCell to all four of them. I have created one TableViewCell class also.
right now I am getting this error
fatal error: unexpectedly found nil while unwrapping an Optional value
Here is my code
let NUMBER_OF_STATIC_CELLS = 3
var labels = ["label1","label2","label3"]
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return labels.count + 1
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell: TestViewCell!
print(indexPath.row)
if (indexPath.row == 0) {
cell = tableView.dequeueReusableCellWithIdentifier("static1", forIndexPath: indexPath) as! TestViewCell
}
if (indexPath.row == 1) {
cell = tableView.dequeueReusableCellWithIdentifier("static2", forIndexPath: indexPath) as! TestViewCell
//cell.cardSetName?.text = self.cardSetObject["name"] as String
}
if (indexPath.row == 2) {
cell = tableView.dequeueReusableCellWithIdentifier("static2", forIndexPath: indexPath) as! TestViewCell
//cell.cardSetName?.text = self.cardSetObject["name"] as String
}
cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! TestViewCell
cell.testLabel.text = "row \(indexPath.row)" // return test rows as set in numberOfRowsInSection
return cell; //getting error here
}
UPDATE
okay error has been removed. Now I come up another problem. I want to know if there are total 4 rows. 3 are static and then rest of them will be dynamic. what would be total number of rows then. I am doing this
labels.count+2 //2 static cells(0 1 2 )
But I am getting fatal error: Array index out of range
You have no code to return a cell if the row is 4 or more. Change this:
if (indexPath.row == 3) {
to this:
if (indexPath.row >= 3) {
Or just change it to:
else {
your code above has some of errors:
when indexPath.row == 2 it should be "static 3".
put the last line of the code that defines the "cell" inside an else statement, to stop it from always executing. When you have done that, it should work.
like this:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell: TestViewCell!
if (indexPath.row == 0) {
cell = tableView.dequeueReusableCellWithIdentifier("static1", forIndexPath: indexPath) as! TestViewCell
}
if (indexPath.row == 1) {
cell = tableView.dequeueReusableCellWithIdentifier("static2", forIndexPath: indexPath) as! TestViewCell
}
if (indexPath.row == 2) {
cell = tableView.dequeueReusableCellWithIdentifier("static3", forIndexPath: indexPath) as! TestViewCell
}
else{
cell = tableView.dequeueReusableCellWithIdentifier("dynamic\(indexPath.row-2)", forIndexPath: indexPath) as! TestViewCell
}
cell.testLabel.text = "row \(indexPath.row)"
return cell
}

Returning two cells in cellForRowAtIndexPath using segmented control

I have a segmented control above two UITableViews, one of which is layered above the other. When segmentedControl.selectedSegmentIndex == 1 one of those table views will hide. The problem, however, is that I can't configure the custom cells of the second table view in my one cellForRowAtIndexPath function. I keep getting: Variable 'cell' used before being initialized.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
switch segmentedControl.selectedSegmentIndex {
case 0:
var cell = tableView.dequeueReusableCellWithIdentifier("CellOne", forIndexPath: indexPath) as! CellOne
return cell
case 1:
var cellTwo = tableView.dequeueReusableCellWithIdentifier("CellTwo", forIndexPath: indexPath) as! CellTwo
return cellTwo
default:
var cell: UITableViewCell
return cell
}
}
You have default branch with not initialized cell variable that you returns. I would recommend the change like following:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let result: UITableViewCell
if (segmentedControl.selectedSegmentIndex == 0) {
var cellOne = tableView.dequeueReusableCellWithIdentifier("CellOne", forIndexPath: indexPath) as! CellOne
//Configure here
result = cellOne
} else {
var cellTwo = tableView.dequeueReusableCellWithIdentifier("CellTwo", forIndexPath: indexPath) as! CellTwo
//Configure here
result = cellTwo
}
return result
}

Resources