Resize iOS tableviewcell contaning several views - ios

I'm looking for a way to resize a tableView cell with a more complex layout.
To simplify the description of the problem the tableview cell consists of three views. One view that defines the "background" of the cell and two additional views (each of them with a height 45% of the background cell.
One of these additional views is tagged to the top of the background view the other one to be bottom.
If the user taps on the tableview cell it should shrink in a way that only the top view is visible and after an additional user tap it should resize to its full size again.
The user tap is handled by the function
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
and the resizing is done by
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
Unfortunately, after shrinking the tableview cell, both addition views are displayed with half of their original hight.
var selectedCell = -1
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
tableView.deselectRow(at: indexPath, animated: true)
if (selectedCell != indexPath.row)
{
selectedCell = indexPath.row
}
else {
selectedCell = -1
}
tableView.beginUpdates()
tableView.endUpdates()
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if (indexPath.row == selectedCell)
{
return 65
}
else {
return UITableView.automaticDimension
}
}
I'm now looking for a way to change the code in a way, that after shrinking only the upper view is visible.
Example picture of the fully visible cell
In the example picture, after shrinking the tableview cell should only display the red view.
Thanks in advance
Patrick

The reason is that when the cell is shrunk, your views (since their sizes are defined relative to parent's size) also gets shrunk and are visible with half size. They are not really gone/hidden from the view. What you need is to hide/remove them from the view.
It looks from your description that you are using plain constraints to achieve this. This can be done by just using constraints but its a lot more work. So I will mention two ways to get this done:
Using just constraints
When the user taps your cell, you need to make the height of the bottom view 0. Also, if you do not want the middle 10% part to show when the cell shrinks, you would need to create an additional constraint from the bottom of the top view to the bottom of the contentView of the cell. Similarly, your bottom view will also have two height constraints, one for 45% and one for 0.
The idea is to have both these constraints at the same time but with different priorities. The initial constraint will have higher priority and the other one will have a lower priority. Although these constraints will be contradictory, iOS will take the higher priority one to render the view.
Next, you would need to toggle the active property of the higher priority constraint on user tap which will in turn let iOS use the lower priority constraint to render the view.
Use stackview:
iOS 9 introduced stack view which is a helper view. It basically, handles constraints for you, at least some part of it. When you hide one view from the stack view's children, stack view will automatically make the height of that view to be 0. read up more about vertical stack view and you will get the idea.

Related

Hiding the internal views and auto adjust the height of the cell swift

According to the following screenshot I have a table view cell with couple of stack views inside the cell. I want to hide the red circled stack view when it's inside label values are empty. To achieve this specific thing what I have done is just hiding the related stack view as the following screenshot. The related stack view is hiding. That's fine. But I want it to be hidden that empty area and adjust the height of the cell accordingly. I am not using the following function as well.
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 150
}
I have used the following function as well. But it's still the same.
override func viewWillAppear(_ animated: Bool) {
SalaryDetailTableView.rowHeight = UITableViewAutomaticDimension
SalaryDetailTableView.estimatedRowHeight = 65.0
}
Could anyone please help me to sort out the issue ?
https://imgur.com/a/hMYA3qT
https://imgur.com/a/Dye8pBT
From your first screenshot I can see that you are using AutoLayout. Wrap all your stack views in a base stack view, pin it to the cell and check and fix your other constraints if needed. After that, you need to specify
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
If your constraints are right, AutoLayout will take care of collapsing or expanding the cell when you hide your inner stack view.

How to show an image view only if the model object contains image url

So I am creating a facebook feed of sorts and I am fetching text, url for images/videos and username etc. The cell I have created has an image view, text view and label. I am sizing the cell according to the text view content size.
Now lets say the user has only posted text and there no image with it. I want the cell to be sized so that there is no image view in the cell. Only the text view and thats it.
Can this be done using just one cell/model/adapter or do I need custom cells and show them according to the model.
A little light on how this could be done would be appreciated.
You just need one cell and there are different ways of achieving this. A simple oen is using a UIStackView inside the cell.
When one element of the UIStackView is hidden, it adapts it size. If you place constraints from to the stack view to the cell, the cell with be smaller/larger depending if you show the image. You just have to work with constraints.
Also, using UIStackView or not, remember to specify on your tableView that the size of the cell will be dynamic by doing:
self.tableView.rowHeight = UITableViewAutomaticDimension
Create a outlet of image height constraint, and then check if your URL is blank or invalid then make height constraint constant 0 otherwise give it appropriate height.
And make sure you have implemented both the methods :
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 100; // Put estimated height
}
And another one is,
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}

Why UITableViewCell shrinks contentView?

Here is the UITableView that I want to make:
In order to achieve this, I divided contentView of cell into three parts.
1 - fromView: contains start time and class type. Height will be 30% of contentView
2 - infoView: contains clock image, class name and professor's name. Height will be 40% of contentView.
3 - toView: contains end time and location. Height will be space that left(30%).
Firstly, before going to details, I decided to show this containers only. For understanding a problem, I painted them(green, red, blue respectively).
After adding them into the contentView, here are the constraints I gave to those containers:
fromView.easy.layout(Top(), Left(), Right(), Height(*0.3).like(contentView))
infoView.easy.layout(Top().to(fromView), Left(), Right(), Height(*0.4).like(contentView))
toView.easy.layout(Top().to(infoView), Left(), Right(), Bottom())
Seems like everything is right. After launching, I thought that I will see them, but here is what happened:
I thought that cell does not know what size it needs to be, so I decided to implement following function:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 150
}
After this, UITableView showed containers pretty correctly than before. But, I plan to add some UILabels and other views into that containers(look at first picture). In other words, I don't know what size a cell needs to have. It needs to be kind of dynamic. Anyway, I tried to add those labels into the containers, maybe after that, a cell will figure out the the height. Here is how I did it:
func setupViews() {
fromView.addSubviews(fromTime, classType)
toView.addSubviews(toTime, room)
infoView.addSubviews(clockImage, teacherName, subjectName)
contentView.addSubviews(toView, fromView, infoView)
}
func setupConstraints() {
fromView.easy.layout(Top(), Left(), Right(), Height(*0.3).like(contentView))
infoView.easy.layout(Top().to(fromView), Left(), Right(), Height(*0.4).like(contentView))
toView.easy.layout(Top().to(infoView), Left(), Right(), Bottom())
fromTime.easy.layout(CenterY(), Left(8))
fromTime.setContentHuggingPriority(.required, for: .horizontal)
fromTime.setContentCompressionResistancePriority(.required, for: .horizontal)
classType.easy.layout(CenterY(), Left(8).to(fromTime))
clockImage.easy.layout(CenterY(), Left(16), Width(24), Height(24))
subjectName.easy.layout(CenterY(-8), Left().to(classType, .left), Right())
teacherName.easy.layout(Top(8).to(subjectName), Left().to(subjectName, .left), Right())
toTime.easy.layout(CenterY(), Left(8))
toTime.setContentHuggingPriority(.required, for: .horizontal)
toTime.setContentCompressionResistancePriority(.required, for: .horizontal)
room.easy.layout(CenterY(), Left(8).to(toTime))
}
But anyway, problem appeared again like this:
I think, the problem is that I give height to containers according to height of contentView, but contentView does not know its height. But I also don't know what height it needs to have, cause it needs to depend according to size of labels. So, how to solve this problem?
Don't set heights for the containers. You have to make sure the top and bottom of the cell are connected through constraints. My recommendation is to just get rid of the containers, add all the labels and images to the cell directly, and create constraints like this.
Then set tableView.rowHeight = UITableView.automaticDimension, or return UITableView.automaticDimension from tableView(_:heightForRowAt:)
Try by giving height constraint to your main view inside your cell.
Look like what you want is a dynamic height calculated based on the content's AutoLayout. Update the delegates to return the UITableViewAutomaticDimension for the heightForRowAt and some good estimation, e.g., 150 for the estimatedHeightForRowAt:
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 150
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
Then you can just set the content's size using autolayout, and the table will calculate the height of the cells for you dynamically.
Estimated height is used for scrolling purposes - a good estimate will provide you a good scrolling experience (dynamic height is calculated only when the cell is being displayed, thus for the yet undisplayed cells the tableView will use estimation when scrolling).

Dynamic tableViewCell height

I have a tableViewCell with a label inside that could be multiple lines tall. I've set the label's Lines property to 0. However, when I make the label's text have multiple lines the text gets cut off. Here's how I've set up my storyboard:
Does anybody know how I made the table's cells just tall enough to contain the labels within?
Setting Dynamic Cell height procedure
Pin the label from top and bottom. Please refer following Screen shot
Set numbers of line to 0 of the label as from property inspector of xcode, it can be done from code too please refer following screen shot
Implement delegates of table view mentioned below
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 50 // also UITableViewAutomaticDimension can be used
}
You are missing the bottom constraint from the label to the table view cell (as far as I can tell). In order to make autolayout know how large the height of the cell has to be, you need to supply those constraints.
In addition do not forget to provide the estimatedRowHeight to the table view. If that value is not given, automatic cell sizing will not work.

Dynamic UITableViewCell not resizing according to the content

Senario A:
If I set the label content in cellForRowAtIndexPath, the cell correctly get resized.
Senario B:
If I change the text content in custom action in cell, the cell sized does not get changed.(I do call setNeedsLayout + layoutIfNeeded)
How to fix this?
EDIT:
1) I have set,
myTableView.estimatedRowHeight = 71.0
myTableView.rowHeight = UITableViewAutomaticDimension
2) I have correctly added auto layout constraints.
I was running into this issue, and my problem was that I was constraining the content to self (the UITableViewCell) and not to self.contentView (the contentView OF the cell). Hope this helps someone else who has built their cells all in code!
In my case, the cell's custom size was enabled:
After you change the text of the cell, just reload that particular cell or simply call mainTableView.reloadData().
To reload that cell-
//indexPath is indexPath of cell you just changed label of
mainTableView.reloadRows(at: indexPath, with: .automatic)
In my case, in the same cell I had an imageView in the top left corner with a "center vertically in container" constraint, and a "top space container" constraint.
Obviously to satisfy this two constraint the cell must have an height equal to:
(height of the imageView / 2) + (length of the top space container constraint).
This height is not enough to fit the label text, so my label had only 1 line visible.
After I have deleted the imageView top constraint all went to the right place, in my case i wanted the image to be centered, if the image had to stay in the top left corner I had to take off the "center vertically in container" constraint.
I hope this can help someone.
First of all, I don't specifically know what was your action on UITableViewCell. So, I assume I do that in UITableViewCell selection.
The below answer only work on iOS 9 and above
But, for some reason, it failed to do it in iOS 8 until it scroll. So, I will update the answer for iOS 8.
I have seen you have used UITableView's estimatedRowHeight and rowHeight at your project. So,
Please check the following
Make sure UITableView's estimatedRowHeight and rowHeight include inside viewDidLoad()
Make sure your UILabel lines set to 0
Make sure there is no constraints about height for your UILabel and let the constraints be like that :
If there are other component also included, make sure only bottom and top space constraints included or top space to container margin and bottom space to container margin.
Every time that you want to update the cell, you have to reload tableView no matter what your current situation will be.
So, don't say anything yet before you try this sample project, #Rikh answer still work. May be you are going in wrong direction. Here's the solution. Please do as I said steps by steps and let me know if that didn't work out. You might need to share your sample project which is causing.
Sample Demo - DynamicCellDemo
UPDATE for iOS 8 : update the following code for iOS 8 users
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if #available(iOS 9, *) {
// do nothing
} else {
tblDynamic.reloadData()
}
}
what you can do is set the AutoLayout constraints for the label in present in the cell from all the sides that is from Top, Bottom, Leading and Trailing. Then add the following UITableViewDelegate method to your class.
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 500
}
This will do the job. As now content in table view cell automatically adjusts the height of the cell.
Try this if it works:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
Add the following in your viewDidLoad()
-(void)viewDidLoad{
[super viewDidLoad];
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 140
}

Resources