tableview cells mix after scrolling (swift) - swift

I made a table view that fills the cells with some data, which works fine.
The problem is that, when I scroll the table, all the cells get mixed, do you know a way to stop this?
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let celda = tabla_reportes.dequeueReusableCellWithIdentifier("celda", forIndexPath: indexPath) as! celdaReportes
switch indexPath.section {
case 0:
celda.datos = tipos
celda.back = UIColor.lightGrayColor()
case 1:
celda.index = indexPath.row
celda.datos = fillArrayWithData(datos[indexPath.row] as! NSDictionary)
celda.back = UIColor.clearColor()
default:
break
}
celda.delegate = self
return celda
}

you should update field of the cell. maybe you here UITableViewCell reuse good practice can help you

Because the cells are reused for performance. Eg, when you scroll up the tableview and the number 1 cell disappears, the number 10 cell is actually your number 1 cell. Whatever you set in number 1 cell and the value is not updated stays with number 10 cell and so on...Try put a blank value inside your cell if it has no value.

Related

UITableViewCell title positioning

I am creating an app with UITableViewCells that expand when selected. However, when the cell expands, the title (and subtitle) are moving downwards to stay centered vertically in the cell. I'd like them to stay where they were originally but can't figure out how to do it.
I have tried setting the frame manually with cell.textLabel?.frame = myRect, but this does nothing. I am unable to find any constraints, as print(cell.textLabel?.constraints) returns optional([]).
Here are my normal cells and my expanded cells.
My code for creating the cells:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier")
if cell == nil {
cell = UITableViewCell(style: .value1, reuseIdentifier: "reuseIdentifier")
}
cell!.textLabel?.text = content[indexPath.row]
cell!.clipsToBounds = true
cell!.selectionStyle = .none
cell!.detailTextLabel?.text = detailContent[indexPath.row]
return cell!
}
Any help would be greatly appreciated.
You are using a standardized cell, the title and detail are fixed, i think you need make a custom cell for your project
Look this project I make a simple test with customized cell
https://github.com/TexugoProgramador/Teste-Celula-Customizada

adding Button in last tableView Cell shown twice when scrolling

Using Swift4, iOS11.2.1, Xcode9.2,
I successfully added a custom button to the last cell of a tableView. (the button is used to add cells in the tableView - this also works fine...) - see Screenshot-1.
But now the issue: When adding more cells (i.e. more than fit in the tableView-height), and if you scroll to the top, then there is a second cell that shows this button - see Screenshot-2.
(see code below....)
Here are the two screenshots:
How can I get rid of the extra button ????
(it seems that a cell is reused and the button is partly drawn. The cut-off can be explained since the height of the last cell is made bigger than the other cells. But still, there shouldn't be parts of buttons in any other cell than the very last one...even when scrolling up).
Any help appreciated !
Here is my code (or excerts of it...):
override func viewDidLoad() {
super.viewDidLoad()
// define delegates
self.fromToTableView.dataSource = self
self.fromToTableView.delegate = self
// define cell look and feel
self.addButton.setImage(UIImage(named: "addButton"), for: .normal)
self.addButton.addTarget(self, action: #selector(plusButtonAction), for: .touchUpInside)
//...
self.fromToTableView.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
guard let resItems = resItems else {
self.rowCount = 0
return 0
}
self.rowCount = resItems.count - 1
return resItems.count
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if(indexPath.row == (self.rowCount)) {
return 160
} else {
return 120
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = fromToTableView.dequeueReusableCell(withIdentifier: "SettingsCell") as? SettingsCustomTableViewCell
// configure the cell
cell?.configureCell(tag: indexPath.row)
// fill cell-data
if let resItem = self.resItems?[indexPath.row] {
cell?.connectionItem = resItem
}
// add addButton to last cell
if(indexPath.row == (self.rowCount)) {
// add addButton
cell?.contentView.addSubview(self.addButton)
// scroll to last cell (that was just added)
// First figure out how many sections there are
let lastSectionIndex = self.fromToTableView.numberOfSections - 1
// Then grab the number of rows in the last section
let lastRowIndex = self.fromToTableView.numberOfRows(inSection: lastSectionIndex) - 1
// Now just construct the index path
let pathToLastRow = IndexPath(row: lastRowIndex, section: lastSectionIndex)
// Scroll to last cell
self.fromToTableView.scrollToRow(at: pathToLastRow as IndexPath, at: UITableViewScrollPosition.none, animated: true)
}
return cell!
}
You already figured out what the problem is :)
As you say here:
it seems that a cell is reused and the button is partly drawn
And that is exactly what happens here. You have a pool of UITableViewCell elements, or in your case SettingsCustomTableViewCell. So whenever you say:
let cell = fromToTableView.dequeueReusableCell(withIdentifier: "SettingsCell") as? SettingsCustomTableViewCell
Then, as the name implies, you dequeue a reusable cell from your UITableView.
That also means that whatever you may have set on the cell previously (like your button for instance) stays there whenever you reuse that specific instance of a cell.
You can fix this in (at least) three ways.
The Hack
In your tableView(_:cellForRowAt:indexPath:) you have this:
if(indexPath.row == (self.rowCount)) {
//last row, add plus button
}
You can add an else part and remove the plus button again:
if(indexPath.row == (self.rowCount)) {
//last row, add plus button
} else {
//not last row, remove button
}
The Right Way
UITableViewCell has the function prepareForReuse and as the documentation says:
If a UITableViewCell object is reusable—that is, it has a reuse identifier—this method is invoked just before the object is returned from the UITableView method dequeueReusableCell(withIdentifier:)
So, this is where you "reset" your custom table view cell. In your case you remove the plus button.
You already have a custom UITableViewCell it seems, so this should be a small change.
The Footer Way
As #paragon suggests in his comment, you can create a UIView and add that as a tableFooterView to your UITableView.
let footerView = YourFooterViewHere()
tableView.tableFooterView = footerView
Hope that helps

How to add a custom cell to a dynamicly generated tableview?

Nearly during a week i'm trying to figure out how i can append a static-/custom tableviewcell to a dynamically generated tableview. I'm populating the cells based on the data i'm getting from the database. Basically what i'm trying to accomplish is like the following picture from the app ClassDojo:
As you may know and see, you can add add as many groups as you want with the ClassDojo app, but the latest cell, in this case Voeg een nieuwe klas toe, will always stay at the bottom of the tableview. That's exactly what i'm trying to do.
What i tried to do till this moment is trying to calculate the latest cell in the tableview and trying to append my custom cell, but unfortunately i couldn't get my head around it.
I would really appreciate if someone can help me out with this.
Thanks in advance.
Please let me know if you guys need any code.
---------EDITED POST---------
I did accomplish to assign my custom cell thanks to #Slayter, but now i'm facing with the problem that my custom cell is immediately overwritten by my dynamically created cells (with Alamofire).
Any help would be appreciated.
ClassDojo iOS Engineer here! More than happy to share how we do this.
Like Slayter mentioned, we do
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myData.count + 1 // Add one for your custom cell
}
But in addition we also do the following:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = nil;
if self.indexIsForCustomCell(indexPath) {
cell = tableView.dequeueReusableCellWithIdentifier(CustomCell.reuseIdentifier)
// Additional configuration
} else {
cell = tableView.dequeueReusableCellWithIdentifier(RegularCell.reuseIdentifier)
// Additional configuration
}
return cell
}
Where CustomCell looks something like this (not exact):
class CustomCell: UITableViewCell {
static let reuseIdentifier = "CustomCell"
// More code
}
And regular cell looks like:
class RegularCell: UITableViewCell {
static let reuseIdentifier = "RegularCell"
// More code
}
The reason your dynamic cells are getting overwritten is because of the line
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as UITableViewCell
Notice that you are using the same cellIdentifier regardless of whether or not it is the custom cell or the regular cells. This means that most likely you are doing the following:
self.tableView.registerClass(RegularCell.class, forIdentifier: "new group")
self.tableView.registerClass(CustomCell.class, forIdentifier: "new group")
When you should be doing:
self.tableView.registerClass(RegularCell.class, forIdentifier: RegularCell.reuseIdentifier)
self.tableView.registerClass(CustomCell.class, forIdentifier: CustomCell.reuseIdentifier)
OR
self.tableView.registerClass(RegularCell.class, forIdentifier: "regularCellReuseIdentifier")
self.tableView.registerClass(CustomCell.class, forIdentifier: "customCellReuseIdentifier")
By using the same identifier key, you are telling the UITableView to treat both cells as the same type. So when it needs to reclaim memory for a new cell being drawn on screen, it's going to use RegularCell and CustomCell interchangeably.
Hope this helped and thanks for checking out our App!
================= EDIT =================
Realized that I forgot to add the indexIsForCustomCell method. Here it is:
func indexIsForCustomCell(indexPath : NSIndexPath) -> Bool {
if self.myData.count > 0 {
return indexPath.section == 0 && indexPath.row == (self.myData.count+1)
}
return indexPath.section == 0 && indexPath.row == 0
}
It's pretty simple. You just need to tell the tableView to expect one more cell than what is in your data source.
Example
In your numberOfRowsInSection method, you will have something like this:
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myData.count + 1 // Add one for your custom cell
}
Then in your cellForRowAtIndexPath method you just need to add some custom logic for that indexPath
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as UITableViewCell
if indexPath.row < myData.count {
// configure cell as normal
} else {
// Add your custom cell logic here
}
return cell
}

problems with UITableView and reuseablecells

so i am experiencing two problems right now with my (pretty basic) tableview. i will paste my code below but here is the general gist: i'm making a dictionary app, so each cell contains two labels for the two languages. if a cell gets tapped, i'd like for: 1. cell height increase, 2. font size increase, 3. text align center, 4. cell background change color.
i've also included code to 'reset' a selected cell if it is selected a second time - this seems to be causing my second problem
the problem is when a cell is selected:
those four things happen to cells which are currently offscreen.
the program crashes when: i select cell 0, the cell changes which is fine, but when i click on any cell that is offscreen at the time (say cell 10) the program will crash giving me this error:
fatal error: unexpectedly found nil while unwrapping an Optional value
code:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
// reset previous cell but only if selectedRow isn't -1 which is initial value
if selectedRowIndex != -1{
let previousCell = convoTable.cellForRowAtIndexPath(NSIndexPath(forRow: selectedRowIndex, inSection: 0)) as! PhraseTableViewCell
previousCell.backgroundColor = UIColor.whiteColor()
previousCell.lang2Label.font = UIFont(name: (previousCell.lang2Label?.font?.fontName)!, size: (previousCell.lang2Label?.font?.pointSize)! - 5)
previousCell.lang2Label.textAlignment = .Right
}
//make changes to new cell that was pressed upon
if selectedRowIndex != indexPath.row{
print("new cell got selected")
cellSelected = true
// save the selected index
self.selectedRowIndex = indexPath.row
let cell = self.convoTable.cellForRowAtIndexPath(indexPath) as! PhraseTableViewCell
cell.lang2Label.font = UIFont(name: (cell.lang2Label?.font?.fontName)!, size: (cell.lang2Label?.font?.pointSize)! + 5)
cell.backgroundColor = UIColor.cyanColor()
cell.lang2Label.textAlignment = .Center
// update the height for all the cells
self.convoTable.beginUpdates()
self.convoTable.endUpdates()
cell.lang1Label.frame = CGRectMake(3,0, cell.bounds.width, cell.bounds.height/3)
cell.lang2Label.frame = CGRectMake(-3,cell.bounds.height/3, cell.bounds.width, cell.bounds.height/3 * 2)
}
else {
print("same cell pressed")
cellSelected = false
selectedRowIndex = -1
}
self.convoTable.beginUpdates()
self.convoTable.endUpdates()
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! PhraseTableViewCell
cell.lang1Label.text = items![indexPath.row].eng
cell.lang2Label.text = items![indexPath.row].prs
return cell
}
thank you all very much, this help is much appreciated.
so there is no real answer to the questions i posed, the best workaround is to just have another array which maintains the states in the cells. sucks i know but really the best "solution"

Putting multiple type TableViewCells in the same Table View

I have a table view that is currently displaying a type of tableViewCell creating in a XIB File and I want to display a different type of XIB file in the table View at random Places. e.g. Like having Ad display Cells in a tableview at Random Post positions.
How can I do this. I currently have the following code for displaying one Cell:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cardioCell:CardioItemsTableViewCell! = tableView.dequeueReusableCellWithIdentifier("CardioItemsTableViewCell") as? CardioItemsTableViewCell
cardioCell.cellNameLabel.text = "Test String"
return cardioCell!
}
I have this image of the apple news App with one cell without an image. In my instance though, the cell is completely different and has a different layout to the other cells.
Any ideas would be much appreciated.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.row % 2 == 0
let cardioCell:CardioItemsTableViewCell! = tableView.dequeueReusableCellWithIdentifier("CardioItemsTableViewCell") as? CardioItemsTableViewCell
cardioCell.cellNameLabel.text = "Cells with even number of row"
return cardioCell!
else if indexPath.row % 2 != 0
let otherTypeCell:OtherTypeTableViewCell! = tableView.dequeueReusableCellWithIdentifier("OtherTypeItemsTableViewCell") as? OtherTypeTableViewCell
otherTypeCell.cellNameLabel.text = "Cells with odd number of row"
return otherTypeCell!
}
You can not load cells randomly. You need to have a certain logic based on which you can load different types of tableViewCell.