How to change height of UITableViewCell when constraint changes? - ios

My question is related to this one: How to change height Constraint of UIView in UitableviewCell when using UITableViewAutomaticDimension
The solution there does not seem to be working for me.
In the image above i have a simple cell.
On tap of cell, i want to change the constraint of the redView to be larger. This should then automatically change the height of the cell.
I already have the height constraint of the cell set to an #IBOutlet and i think i am correctly changing the size of the cell, but it is not working.
Here is my sample app that is not working. Any help? SampleApp - for Xcode 9.3

You need to set a bottom constraint to the red view so auto-layout can stretch the cell after setting the constant value
extension ViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "c", for: indexPath) as! customcell
configure(cell: cell, indexPath: indexPath)
cell.redview.backgroundColor = .red
cell.selectionStyle = .none
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath) as! customcell
cell.constraint.constant = data[indexPath.row] == "contracted" ? 30 : 200
data[indexPath.row] = data[indexPath.row] == "contracted" ? "expanded" : "contracted"
tableView.reloadData()
}
func configure(cell: customcell, indexPath: IndexPath) {
let data = self.data[indexPath.row]
if data == "expanded" {
cell.constraint.constant = 200
} else {
cell.constraint.constant = 30
}
cell.layoutIfNeeded()
}
}

Calling the below will recalculate the heights.
[tableView beginUpdates];
[tableView endUpdates];

Related

I want to use two different custom cells from nib in a single table view. The two prototype cells have different heights

I have a situation where I have to use two different prototype cells for a single tableView. These two prototype cells have different heights. When I am trying to run the application I am not getting different heights of the cell. Instead, I am getting overlapping cells. The cells are overlapped on each other at index 2.
I have made two xib for the cells and registered the two cells in tableview.
Screenshot of what I am getting
extension Redeem: UITableViewDelegate, UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 4
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0{
let cell1 = tableView.dequeueReusableCell(withIdentifier: "RedeemCell1", for: indexPath) as! RedeemCell1
return cell1
} else if indexPath.row == 1{
let cell1 = tableView.dequeueReusableCell(withIdentifier: "RedeemCell1", for: indexPath) as! RedeemCell1
return cell1
} else if indexPath.row == 2{
let cell2 = tableView.dequeueReusableCell(withIdentifier: "RedeemCell2", for: indexPath) as! RedeemCell2
return cell2
} else if indexPath.row == 3{
let cell1 = tableView.dequeueReusableCell(withIdentifier: "RedeemCell1", for: indexPath) as! RedeemCell1
return cell1
} else {
let cell1 = tableView.dequeueReusableCell(withIdentifier: "RedeemCell1", for: indexPath) as! RedeemCell1
return cell1
}
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
}
I think, the cells are not layouted correctly.
To define the cell’s height, you need an unbroken chain of constraints
and views (with defined heights) to fill the area between the content
view’s top edge and its bottom edge.
If you use XIBs in cells, you can change simulated size in cell, and see what's happened. The cell should be without broken constraints.
You need to either implement heightForRowAt
func tableView(_ tableView: UITableView,heightForRowAt indexPath: IndexPath) -> CGFloat
if indexPath.row == 0 {
return 200
}
else if {} // and so on
}
or use dynamic height table cells

Tableviewcell height with inside tableView

I have a tableView in which every cell contains another tableView with dynamic row height.
My question is how can I set the row height of the first table to fit the height of the inner tableView?
I'm using this code but it does not work as it should.
var heights: [CGFloat] = []
var loades: [Bool] = []
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return self.heights[indexPath.row]
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "eventCell", for: indexPath) as! EventCell
cell.date.text = ev.date
cell.delegate = self
cell.event = ev
self.heights[indexPath.row] = cell.tv.frame.height
if self.loades[indexPath.row] == false{
self.loades[indexPath.row] = true
tableView.reloadRows(at: [indexPath], with: .none)
}
return cell}
I'd try getting the nested tableview's contentSize in your heightForRow method.
I hope you can access to your tableview.contentSize.height, the contentSize gets the value of all tableview content if your tableview is dynamic, the content of this height you can divide in the number of rows what you have. This is just a simply solution, some like this:
let size = tableView.contentSize.height
let cellSize = size / models.count
where models is the array of objects printed in tableviewcell inside tableview
Recommend:
Put a height constraint inside your cell to your tableView, and try to refresh with de sizeCell sum
cell.tableViewInsideCell.reloadData()
cell.heightConstraint.constant = tableView.contentSize.height
and set
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}

Dynamic table view cell wrong height when using >= constraint

I have cells with dynamic height inside an UITableView.
In interface builder, everything looks good.
These are my constraints:
But on the device, there is added empty space at the top and bottom of some messages. There would be no problem if the label's greater than or equal constraint would be an equality constraint, but the bubbles have the width of the screen.
This is my cell configuration:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row % 2 == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "ReceivedMessage") as! MessageTableViewCell
cell.messageLabel.text = arr[indexPath.row]
cell.setNeedsLayout()
cell.setNeedsDisplay()
return cell
}
else {
let cell = tableView.dequeueReusableCell(withIdentifier: "SentMessage") as! MessageTableViewCell
cell.messageLabel.text = arr[indexPath.row]
cell.setNeedsLayout()
cell.setNeedsDisplay()
return cell
}
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 70
}
Any idea why this is happening? Thanks.

Constraints get invalidated

I'm trying to create a table view cell with a UILabel and a UIImageView.
Here's a screenshot of my view controller when it opens.
When I scroll the cell out of the view or when I tap on the cell, the image view's frame changes.
Here are my constraints:
Here are my dataSource methods:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if(indexPath.section == 0) {
let currentVolume = viewModel.volumes?[indexPath.row];
if let volume = currentVolume {
if (volume.isPurchased == 0) {
let reuseIdentiifier = Constants.ReuseIdentifier.VolumeListTableViewCellReuseId;
let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentiifier, for: indexPath) as! VolumeTableViewCell;
cell.setTitle(title: (volume.name));
return cell;
}
else {
let reuseIdentiifier = Constants.ReuseIdentifier.LockedVolumeListTableViewCellReuseId;
let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentiifier, for: indexPath) as! LockedVolumeTableViewCell;
cell.setTitle(title: (volume.name));
return cell;
}
}
}
return UITableViewCell.init();
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
//Disables the 'selected' cell
tableView.deselectRow(at: indexPath, animated: true);
}
I'm unable to figure out what's going wrong?
I'd be grateful for any help.
First select imageView and add constraints selected in screen shot
and also add "Vertically center constraint"
then select label and add constraints selected in screen short
hope this will work if any problem let me know.

Not Recognizing Clicked Cell UITableView inside UITableViewCell using NIB

I'm writing an app that have a dashboard with multiple cells. One of the cells have a question, but the answer are dynamically filled, so I decided to use a UITableView to handle it.
I set the the UITableViewCell as the delegate and dataSource of the internal UITableView and made the configurations for define the cell and the selected state.
extension SurveyTableViewCell: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
answers = model.getSurveyAnswers()
return (answers?.count)!
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: InsideSurveyTableViewCell.identifier, for: indexPath) as! InsideSurveyTableViewCell
cell.idAnswer.text = alphabetQuestion[indexPath.row]
cell.answer.text = answers?[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 100.0
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.dequeueReusableCell(withIdentifier: InsideSurveyTableViewCell.identifier, for: indexPath) as! InsideSurveyTableViewCell
cell.selectRow()
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
let cell = tableView.dequeueReusableCell(withIdentifier: InsideSurveyTableViewCell.identifier, for: indexPath) as! InsideSurveyTableViewCell
cell.deselectRow()
}
}
But the click inside the cell in the internal UITableViewCell is not recognized. I need to recognize this click to after send the user answer to the server.
I saw some solutions, but using storyboard. I use only nib's on my projects.
But I still tried with an approach that I saw on youtube wich uses storyboard.
On the cell that will use the internal UITableView I declared a function to set the delegate and dataSource of the internal tableView and gave to it a tag.
extension SurveyTableViewCell {
func setTableViewDataSourceDelegate<D:UITableViewDelegate & UITableViewDataSource>(_ dataSourceDelegate: D, forRow row: Int) {
subTableView.dataSource = dataSourceDelegate
subTableView.delegate = dataSourceDelegate
subTableView.reloadData()
}
}
Than on the viewController that manage the outer UITableView:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if tableView.tag == 1 {
let cell = tableView.dequeueReusableCell(withIdentifier: InsideSurveyTableViewCell.identifier) as! InsideSurveyTableViewCell
cell.idAnswer.text = "A."
cell.answer.text = "QUALQUER COISA"
return cell
}
if retrivedCell is SurveyTableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: SurveyTableViewCell.identifier, for: indexPath) as! SurveyTableViewCell
cell.delegate = self
cell.setTableViewDataSourceDelegate(self, forRow: indexPath.row)
cell.setPositionRow(row: indexPath.row - 1)
cell.subTableView.rowHeight = UITableViewAutomaticDimension
cell.subTableView.estimatedRowHeight = 50
return cell
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView.tag == 1 {
return 3
}
var numberOfCells: Int = 0
if cellsToPresent != nil {
numberOfCells = cellsToPresent!.count
}
return numberOfCells + 1
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if tableView.tag == 1 {
let cell = tableView.dequeueReusableCell(withIdentifier: InsideSurveyTableViewCell.identifier) as! InsideSurveyTableViewCell
cell.selectRow()
}
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
if tableView.tag == 1 {
let cell = tableView.dequeueReusableCell(withIdentifier: InsideSurveyTableViewCell.identifier) as! InsideSurveyTableViewCell
cell.deselectRow()
}
}
The selectRow and deselectRow are methods to change the label of the cell of the inner tableView.
But still without success.
if I use the method:
tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath)
The app break complaining that I'm trying to dequeue different cells with the same indexPath.
Thanks for your help.
Don't use
let cell = tableView.dequeueReusableCell(withIdentifier:) in didSelect or didDeSelect methods.
Use
let cell = tableView.cellForRow(at: indexPath)
I hope this will help you.

Resources