When UILabel with multi-line and wrap-word enabled is used on UITableViewCell, UITableView handles the different cell heights without any problem. UILabel calculates its height according to its content and its width.
I implemented a custom view (https://github.com/fthdgn/ios-demo/blob/master/demo/CustomGridView.swift) which also calculates its height according to its content and its width. This view is a simple grid view which determines row count and height according to column count which is determined by available width. However, there are problems when custom view is used on UITableViewCell.
On my demo application (https://github.com/fthdgn/ios-demo),
On the 1st tab [1st image], the custom view is used alone. Its height is correct. Button adds new items and updates it height correctly.
On the 2nd tab [2nd image], the height of the custom view is calculated wrongly. This height is calculated according to placeholder width of the view on the XIB file. This width is smaller than correct width, thus the height is longer than correct value.
Reload button on the 2nd tab [3rd image], reloads table. Custom view calculates and updates itself correctly.
On the 3rd tab [4th image], UILabel calculates its height according to available width from the start.
Why does not my custom view work like UILabel? Are there missing setNeedsLayout, invalidateIntrinsicContentSize calls?
I updated the code to show only 1 row with 11 cells on UITableView.
The width of Grid View on on XIB file is 300, because the selected device is 4S. The simulator I use is 11 Pro Max, it is wider than 4S.
intrinsicContentSize and layoutSubviews call prints these:
content (-1.0, 50.0) frame (300.0, 0.5)
First calls intrinsicContentSize. Frame is what is on the XIB,
constraints does not resized the view yet. This width requires 50
height to place cells.
layout (394.0, 50.333333333333336)
Calls layout, the height is determined according to intrinsicContentSize, width determined by UITableViewCell constraints. It is wider then the value which intrinsicContentSize is calculated by.
content (-1.0, 20.0) frame (394.0, 50.333333333333336)
Because layout changed, I invalidate the intrinsicContentSize. New intrinsicContentSize value is calculated according to correct width. New height is 20.
layout (394.0, 50.333333333333336)
Something triggers layout. Eventhough last intrinsicContentSize.height is 20, it still uses 50.
content (-1.0, 20.0) frame (394.0, 50.333333333333336)
Layout invalidates intrinsicContentSize. intrinsicContentSize is recalculated but its value does not change.
After these calls, event though the last intrinsicContentSize wants 20 height, frame continues to be 50. You can see at 2nd screenshot, the row with 11 cell is longer than what it should be.
Related
I'm trying to create a custom dialog with a UITableView inside.
What I want to achieve is to have the lowest height possible, meaning it should be only wrapping the tableview.
BUT when the tableview has too many items (meaning its height is bigger than the screen), I want the dialog to have 20 px margin from the screen top and bottom.
So if the tableview has 2 items, the height of the dialog should be for example 20 px. But if the tableview has 200 items, the height of the dialog should take almost the entire screen height and have its content scrollable.Dialog with few items
Dialog with multiple items
Currently if the tableview has multiple items, I can only see some items and the top and bottom of the dialog disappear.
Thanks.
EDIT: I forgot to mention I intend to achieve this using the storyboard only, using constraints and changing priority in content hugging and compression.
GOT IT WORKING!
All I had to do was change the top and bottom constraints from "=" to ">=".
So now it allows the dialog to have a smaller height but not bigger than the screen. No code needed.
Thanks everybody for your help.
My answer will include several parts. They are solving a more general question of how to efficiently work with dialogs.
To create dialog use UIViewControllerTransitioningDelegate, UIPresentationController and UIViewControllerAnimatedTransitioning. It's a long way, but it will make your dialogs presentation reusable.
To determine size of the dialog with table inside of it, set peferredContentSize property of your dialog view controller. This property will be used by UIPresentationController to set dialog height. Before setting peferredContentSize you can adjust the height to the margins you like.
To calculate size of table view you can use several options. table.layoutIfNeeded() worked for me.
Update: check out the article I wrote about this approach.
You need to set the tableView height dynamically, so give it two position constraints (ie centerX, centerY), a fixed width constraint, and then a height constraint set to some arbitrary constant (0). Take an outlet to the height constraint. Whenever you update the data that backs the tableview you take the min of the calculated tableview height and the superview height and set the height constraint constant to that value, then call setNeedsLayout on the tableView's superview. This way the tableview will either be the height of its content, or if the content is too big, the height of the superview and it will scroll.
tableViewHeightConstraint.constant = min(superview?.bounds.size.height ?? 0, numberOfCells * heightPerCell + headerAndFooterSize)
superview?.setNeedsLayout()
I guess that you know how many items you have before present the dialog and I guess that you dialog is a custom view (xib)
if this premises are true, you can resolve whit this line.
let dialog = CustomDialog(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: (heighCell * elements) + (heightButtonClose) + (heighTitle))
dialog.center = self.view.center
let me know if you have doubts
I'm working to make a chat bubble like in iMessage. I have to use UITextView to get advantage of DataDetectors to detect urls, phone numbers and addresses automatically. My view hierarchy is like;
-UITableViewCell
-ContentView
-ChatBubbleContainerView
-UITextView
ChatBubbleContainerView have constraints of
10-pt left, right, top and bottom margins --> ContainerView
UITextView have constraints of
10-pt of left, right, top and bottom margins --> ChatBubbleContainerView
I want to achieve a behaviour to make the text view automatically expand its height and width according to its content inside e.g. text. But the text view has no intrinsic content size.
These are what i have tried and got wrong results
Overriding the intrinsicContentSize property of text view but it's
not working too.
Giving a width constraint to the ChatBubbleContainerView and changing it to result of sizeThatFits() method
Giving a width constraint to the ChatBubbleContainerView and changing it to result of text.boundingRect method
These are the results i've got
It's not working properly and the cell is not laying out as expected as i scroll the table view
It's working but the initial width of cell in the nib is 375-pt which is good for 4.7 inch devices but when i switch to devices like iPhone SE which has 4 inches at the initial table view load or cell insertion, the cell's width is calculating according to 375-pt not 320-pt. After scrolling cell, the layout is laying out as expected.
Same with 2.
When i try same layout with UILabel it's working as expected. As you may know UILabel has intrinsic content size.
Thanks
TextView Contraints are good you need to made some changes on ChatBubbleContainerView constraints.
ChatBubbleContainerView constraint:
top, leading, bottom -> 10 pts
MaxWidth(GreaterThanOrEqual) to contentView, for instance 0.85 or you can set TrailingConstraint with maxConstant(GreaterThanOrEqual to contentView.
you need to set the maxWidth then only ChatBubbleContainerView will be responsive.
I have a custom UITableViewCell I've created programmatically that I want to be the full height of my view. I've overridden "heightForRow", but that doesn't affect the size of the cell's frame on the first load. The current frame loads at 320 width, 44 height which I guess is the default size for a cell. The frame of the cell does not change until I actually scroll the cell off screen then back on to "refresh" it. What should I do to set the cell's frame height to be full screen on the first load?
Reload tableview in viewDidAppear. Best way to handle these kind of scenarios is making cell through xib or storyboard and applying auto layout constraints.
I want to implements a customize, and the view's height can be calculated according to it's content and width. The view's width is arbitrary and in IB usually set leading and trailing offset. The question is how can I get the width of this view and do the layout?
A similar view is UILabel, if set numberOfLines property to 0 it can adjust it's height according to content and width. UILabel has a property preferredMaxLayoutWidth, during constraints update, this property is set to correct width and using these do the work. And the same, when and how this property is set?
For example, i have a grid image which look like this
If width is given, then we can calculate the height of the view. And I have pin it to a cell like this
So the cell's height will adjust according by the grid image view's height.
But how did i know the exact width of the grid image view? The actually result is following when the table view first shown.
I have a custom UITableViewCell subclass, and say there is a UIPickerView inside of it. As far as I know the cell requires constraints on both its top and its bottom so that the height can be calculated. The problem is when I specify only the top and bottom constraints, the height of the cell displayed is like 44. So I added a height constraint for the UIPickerView to be 162, then the console says all these constraints cannot be satisfied simultaneously.
So how can I let the estimated height to be 162 so that the UIPickerView is displayed properly (Without overriding the height function of UITableViewDelegate?)