Expanding Cell in Swift 3 - ios

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.

Related

Is there a way to display a UI Cell a single time and another UI Cell multiple times?

I have a UITableView which represent a questionnaire with Dynamic UI Cells. I've created 2 cells.
The first UI Cell need to be all the time on the top (like a Title/Header) and the second UI Cell need to be Dynamic (this means that the second UI Cell can display 10 or 20 questions). Is there any way to do this ?
I've already created a UITableViewCell for both UI Cells and I linked all the components.
Now I don't know where to specify that Cell 0 to be returned 1 time and Cell 1 to be returned X times.
Also how I keep Cell 0 on the top all the time ?
Here is a printscreen which reflect my issue:
Here is my code:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch indexPath.row {
// Static cell - Checks Left
case 0:
let cell = tableView.dequeueReusableCell(withIdentifier: "checksLeftCell", for: indexPath) as! ChecksLeftTableViewCell
return cell
// Dynamic cell - Questions
case 1:
let cell = tableView.dequeueReusableCell(withIdentifier: Constants.defectAndDamageCell, for: indexPath) as! DefectAndDamageCheckCell
cell.configCell()
cell.delegate = self
return cell
default:
return UITableViewCell()
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
switch indexPath.row {
case 0: return 100
case 1: return 400
default:
return 170
}
}
I expect to have "frozen" cell on the top and the second one to be a dynamic cell.
Thanks if you read this and I hope that I explained well what is in my mind.
This isn't sign of good thinking about design. It looks like you don't need UITableView at all. You can achieve better result using basic UIViews for each component such as Title, question details, tags, etc.
You can help yourself using UIStackView.
Instead of a static cell you can use a Section Header with a custom View inside of it:
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let nameLabel = UILabel(frame: CGRect(x: 10, y: 0, width: tabelView.frame.width, height: 40))
nameLabel.text = //
nameLabel.font = taskNameLabel.font.withSize(14)
//Or add whatever you need here
let sub = UIView(frame: CGRect(x: 0, y: 0, width: tabelView.frame.size.width, height: 40))
sub.backgroundColor = .white
sub.addSubview(NameLabel)
return sub
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: Constants.defectAndDamageCell, for: indexPath) as! DefectAndDamageCheckCell
cell.configCell()
cell.delegate = self
return cell
}
I would try using UICollectionView instead of the tableview.
From UICollectionView, use the UICollectionViewDelegateFlowLayout delegate.
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { #your code for header view }
https://developer.apple.com/documentation/uikit/uicollectionviewdatasource/1618037-collectionview
or
Create a section header in your TableView, and create a custom view in it.
So your TableView would have 1 section header and 1 row.
Good luck.
There are many ways to solve this.
If you want a static view that sticks to the top try this:
Subclass a UIViewController and add a container UIView at the top/leading/trailing. Embed your header labels and content in the content view. Next, add a UITableView and pin it to the leading/trailing/bottom of the superview. Make your view controller the data source and delegate.
In this way, you will still have scrolling within the tableview and can reload the cells as needed while managing your static header content as IBOutlets.
If you use the selected answer with stack views, you will have to implement a scrolling solution by embedding your stack view in a UIScrollView when your content is longer than the screen size. You should probably just stick to using a tableview because that behavior is inherited.

Collectionview in tableivew cell. this is bug of reuse

collectionview in tableviewcell. when tableview reuse cell. collectionview in tableivewcell keep position. this is bug? and how to separate for it. example : cell 1 and cell 10 is collectionview(horizontal). when i scroll cell 1, cell 10 also scroll same postion. please help me.
Create a Dictionary to remember the offset for each cell like:
var contentOffset: [Int: CGFloat] = [:]
When table view cell is being removed from screen store the offset of your collection view like
func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
guard let cell = cell as? YouTableViewCell else {
return
}
contentOffset[indexPath.row] = cell.collectionView.contentOffset.x
}
Now when cell is about to display restore the content offset like:
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
guard let cell = cell as? YouTableViewCell else {
return
}
cell.collectionView.contentOffset.x = contentOffset[indexPath.row] ?? 0
}
It is not a bug, when table cell being reused, it also reuse the content which is the collection view which is scrolled. Keep Googling and check out the links:
ios 8 Swift - TableView with embedded CollectionView
https://medium.com/#gargankit476/multiple-collection-view-in-uitableview-ced7909a5af3

Why in heightForRowAt method my cell is nil?

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if let cell = tableView.cellForRow(at: indexPath) as? ExpandablePlaneTableViewCell {
return 400.0
}
return 75.0
}
I want to change size of my cell but inside of heightForRowAt it cannot see my cell and crashed. When I put there if let check it does not enter inside of the block and just takes 75.
Can anyone tell me what the problem is? It's too strange for me!
I already set delegate to self. So it call the function but cannot detect my cell there.
UPDATE
In my ExpandablePlaneTableViewCell I have a variable:
var exapanded = false
Later in my ViewController: On click on the button in the cell I run my delegate method:
func expandViewButtonTapped(_ sender: ExpandablePlaneTableViewCell, for indexPath: IndexPath) {
sender.exapanded = true
tableView.reloadRows(at: [indexPath], with: .automatic)
}
and after I want to expand it and reload the cell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "expandableCell", for: indexPath) as! ExpandablePlaneTableViewCell
cell.delegate = self
cell.indexPath = indexPath
return cell
}
Do not attempt to get a cell in heightForRowAt. And there certainly is no reason to do so in your case.
You seem to want the height to be one value for certain types of cells and another height for other types.
Simply use the same basic logic you have in cellForRowAt, based on the indexPath, to determine which height to return. In other words, base the decision on your data model, not on the cell.

Swift TableView Crash: "Missing cell for newly visible row"

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.

How to hide specific cells in a static UItableview, in swift?

No article explains it clearly regarding my query, I have three cells in a static table and I want to hide second cell when users taps on first cell. Any kind of help is appreciated.
Although you cannot stop the static table from trying to show your cells, you can set their height to zero, making them effectively invisible:
Add this method to your table view controller delegate class:
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
let cell = super.tableView(tableView, cellForRowAtIndexPath: indexPath)
return cell == myHiddenCell ? 0 : super.tableView(tableView, heightForRowAtIndexPath:indexPath)
}
In the didSelectCellAtIndexPath method, you can set the height to 0 to hide it :
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if indexPath.row == 0 {
let indexPath = NSIndexPath(forItem: 1, inSection : 0)
let secondCell = tableview.cellForRowAtIndexPath(indexPath)
secondCell.frame.size.height = 0;
self.view.layoutSubviews()
}
}
If you want an animation, just put self.view.layoutSubviews() in an UIView animation method UIView.animateWithDuration... etc
For me, setting the height to 0 for some cells and another height for other cells wasn't an option, as all my cells have different height.
I created another cell in Storyboard, and set row height of 0 (in size inspector). Then in the code, I show the cell with height = 0 if I want to hide it, if not, I show the other cell:
if (hideCell) {
let hiddenCell = tableView.dequeueReusableCell(withIdentifier: "hiddenCell",for: indexPath) as! TheWallTableViewCell
return hiddenCell
}
else {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell",for: indexPath) as! TheWallTableViewCell
return cell
}

Resources