Im experiencing a crash when adding new rows to a tableview. In short, the crash log says "Missing cell for newly visible row 3".
Reproduction
1. Add N amount of objects to datasource
2. Manually add the same amount
of cells to tableview
3. Reload and animate using beginUpdates - endUpdates
Known Problem
This crash has already been discussed at question and reported at Apple. Their solution to this problem (not using estimated cell heights) does not work for me as i need 2 different heights for the cells.
My tableview is composed out of 2 different cell classes. Both have their own standard height, configured as such:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.row % (straightsetLogs.count + 1) == 0 {
return 44
} else {
return tableView.rowHeight
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// (exerciseSets.count / straightsetLogs.count) = amount of (super)sets
// (straightsetLogs.count + 1) = amount of cells needed for 1 (super)set
return (exerciseSets.count / straightsetLogs.count) * (straightsetLogs.count + 1)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Headercell: 1 headercell for every (super)set
if indexPath.row % (straightsetLogs.count + 1) == 0 {
// Create header cells once for every set
let headerCell = tableView.dequeueReusableCell(withIdentifier: CellID.setHeaderCell, for: indexPath) as! SetHeaderTableViewCell
return configureHeaderCell(headerCell, forIndexPath: indexPath, totalCellsPerSet: straightsetLogs.count + 1)
} else {
// Create picker cells for rest of rows
let pickerCell = tableView.dequeueReusableCell(withIdentifier: CellID.setPickerCell, for: indexPath) as! SetPickerTableViewCell
// Configure according to set and movement
return configurePickerCell(pickerCell, forIndexPath: indexPath, totalCellsPerSet: straightsetLogs.count + 1)
}
}
In storyboard i've configured the tableview itself to have a row height of 260.0 and for the header cell i've checked the custom row height box and set it to 44.0.
Current Status
The code for determining the correct cells for the index paths works and the crash can be solved by removing the height for row at indexPath code. But then i end up with all cells being 260.0 height.
Goal
I need 44.0 height for header cells and 260.0 height for picker cells without experiencing this crash.
Related
I am a new in-app ios development. I am trying to get a UI like this: -
Here two types of tables are attached with one table view. This design in andriod.
I tried in IOS and take one table view with custom header and in the header I set another table view. Like this:-
And set code like this : -
override func viewWillLayoutSubviews() {
secondview?.frame.size = CGSize(width: tableViews.frame.width, height: secondtableViews.contentSize.height + 50 + tableViews.contentSize.height)
secondview.layer.masksToBounds = true
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView == tableViews {
debugPrint(" first")
return 50
}else{
debugPrint(" second")
return 50
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if tableView == tableViews {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")! as UITableViewCell
cell.textLabel?.text = "skdjfhkjfhjkshfkhsdkfhjsd"
return cell
}else{
let cell = tableView.dequeueReusableCell(withIdentifier: "gcell")! as UITableViewCell
cell.textLabel?.text = "skdjfhkjfh jkshfkhsdkfhjsd"
return cell
}
return UITableViewCell()
}
I change the height of main table view. It workes on some part height do not work according to the content of table view and the data of first table showing on second and second is showing on first. Can any one help me out from this design issue.
you should go with one tableview with TWO sections and TWO different type of cell...!
I have default UITableViewController with 10 row
have UITableViewCell with segment control
If I change segmentController selected index in probably cell : 2, then scroll down this state of segmentController will be in other cells
cells1(selectedIndex: 1 )
cells2(selectedIndex: 1 )
cells3(selectedIndex: 1 )
cells4(selectedIndex: 1 )
cells5(selectedIndex: 1 )
... Select in cell 2 other index (2)
scroll down
and cell 5 have same selected index, but I am not touch them
cells1(selectedIndex: 1 )
cells2(selectedIndex: 2 )
cells3(selectedIndex: 1 )
cells4(selectedIndex: 1 )
cells5(selectedIndex: 2 )
selected 0 row
row 5 is too selected, why?
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.selectionStyle = .none
cell.textLabel?.text = String(indexPath.row)
cell.textLabel?.textColor = .white
return cell
}
UITableView and UICollectionView reuse cells. Which means it only keeps enough cells in memory to show what is on the screen. As you scroll cells off the screen they are fed back in to become the cells that are appearing. You need to properly configure the cells as they re-appear in your override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)
So in your case you need to set the segmented control's selection in cellForRow every time.
This means you will need a way to keep track of what selection has been made on your segmented controls and call that up to set the segmented control correctly when the cell is being shown again.
Reset selection in prepareForReuse method of UITableViewCell by subclassing it.
override func prepareForReuse() {
}
So every time I scroll my tableView it reloads data which I find ridiculous since it makes no sense to reload data as it hasn't been changed.
So I setup my tableView as follows:
func numberOfSections(in tableView: UITableView) -> Int {
return self.numberOfElements
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 6
}
My cells are really custom and they require spacing between them. I couldn't add an extra View to my cell to fake that spacing because I have corner radius and it just ruins it. So I had to make each row = a section and set the spacing as a section height.
My cell has a dynamic height and can change it's height when I click "more" button, so the cell extends a little.
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if self.segmentedControl.selectedSegmentIndex == 0 {
if self.isCellSelectedAt[indexPath.section] {
return self.fullCellHeight
} else {
return self.shortCellHeight
}
} else {
return 148
}
}
And here's how I setup my cell:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = UITableViewCell()
if self.segmentedControl.selectedSegmentIndex == 0 {
cell = tableView.dequeueReusableCell(withIdentifier: String.className(CurrentDocCell.self)) as! CurrentDocCell
(cell as! CurrentDocCell).delegate = self
(cell as! CurrentDocCell).ID = indexPath.section
} else {
cell = tableView.dequeueReusableCell(withIdentifier: String.className(PromissoryDocCell.self)) as! PromissoryDocCell
}
return cell
}
So I have a segmentedControl by switching which I can present either one cell of a certain height or the other one which is expandable.
In my viewDidLoad I have only these settings for tableView:
self.tableView.registerCellNib(CurrentDocCell.self)
self.tableView.registerCellNib(PromissoryDocCell.self)
And to expand the cell I have this delegate method:
func showDetails(at ID: Int) {
self.tableView.beginUpdates()
self.isCellSelectedAt[ID] = !self.isCellSelectedAt[ID]
self.tableView.endUpdates()
}
I set a breakpoint at cellForRowAt tableView method and it indeed gets called every time I scroll my tableView.
Any ideas? I feel like doing another approach to make cell spacing might fix this issue.
A UITableView only loads that part of its datasource which gets currently displayed. This dramatically increases the performance of the tableview, especially if the datasource contains thousands of records.
So it is the normal behaviour to reload the needed parts of the datasource when you scroll.
I have a table view in Swift with headers that expand or contract to show or hide cells, but for some reason there are extra row dividers in the middle of the page for no reason, and the label in the header is not showing up.
class ExpandableHeader: UITableViewHeaderFooterView {
var section: Int = 0
let expandLabel = UILabel()
}
func numberOfSections(in tableView: UITableView) -> Int {
// return number of section in table from data
return list.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// return number of rows in each section from data
return list[section].items.count + 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Create cell with the identifier which was set in the storyboard prototype cell
// set cell data/name from our data object
if indexPath.row < list[indexPath.section].items.count {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = list[indexPath.section].items[indexPath.row].name
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "custom", for: indexPath)
return cell
}
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
// create a header of type of our subclassed header with section number
let headerView = ExpandableHeader()
headerView.expandLabel.text = "+"
headerView.expandLabel.frame.size.height = 30
headerView.expandLabel.frame.size.width = 30
headerView.expandLabel.textAlignment = NSTextAlignment.center
headerView.addSubview(headerView.expandLabel)
headerView.expandLabel.frame.origin.x = view.frame.maxX - headerView.expandLabel.frame.width
// assign selected/current section number to the header object
headerView.section = section
// create Gesture Recognizer to add ability to select header to this cutsom header with an action
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(headerClicked(sender:)))
// add Gesture Recognizer to our header
headerView.addGestureRecognizer(tapGesture)
return headerView
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
// check if row's section expanded parameter is set to true or false. set height of rows accordingly to hide or show them
if list[indexPath.section].expanded == true {
return 44
} else {
return 0
}
}
I added the relevant lines of code above. When you change the row height to 0, should the cell separator lines also be changed to height 0, or hidden?
Why is the header in each section not going all the way to the right side of the screen when the storyboard has it all the way to the right edge?
Is this also why the text label for the header is not being displayed? Because the right edge is getting cut off?
Sorry for the basic questions, I'm still getting the hang of this, so any suggestions would be much appreciated.
To hide extra row divider use below lines of code
let backgroundView = UIView(frame: CGRect.zero)
self.tableView.tableFooterView = backgroundView //instead of tableView give your tableView name
I'm trying to implement an expanding cell using this
here is my heightForRowAt indexPath
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
switch indexPath.row {
case 0 : return 70
case 1 :
let cell = self.tableView(tableView, cellForRowAt: indexPath)
if let datePickerCell = cell as? MyDatePickerCell {
return datePickerCell.datePickerHeight()
}
return 260
case 2 : return 80
default : return 60
}
}
The app gets stuck at
let cell = self.tableView(tableView, cellForRowAt: indexPath)
the UITableView is embeded in a modal UIViewController
why is this happening?
I think what is happening is that your tableView is looking for the heights of your cells before loading them in the first place. So you are trying to retrieve the cell before it is loaded, causing a crash.
To fix this you could create a dummy cell before loading the tableView and using that cell in heightForCell. That's more applicable for calculating variable heights for each cell though. In this case maybe hardcode the start height and then use the cell for heights after that.