Dynamic height for custom dialog in iOS - ios

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

Related

iOS custom view inside table view cell with intrinsic height

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.

Why do my controls get squished inside a scroll view?

I'm building an iOS app and every time I use a scroll view and put other views and controls inside of it this happens:
How do I prevent this from happening? I tried putting the scroll view in a UIView and putting a UIView inside a scroll view and putting other controls inside of the nested views but the same thing still happens. Any ideas on how to fix this?
After you add something inside scrollView, it doesn't know what width and height it will have. Thus, it is unable to correctly display its subviews.
There are some major solutions:
You need to specify correct sizes AND distances both vertically and horizontally.
E.g. scrollView will have 1 label. Then you should specify something like this:
label 1:
leading = 20, trailing = 20, width = 100 (this will solve horizontal dimension)
top = 20, height = 100, bottom = 20 (this will solve vertical dimension)
Of course, labels are self-sizing. So if you specify the text/font/number of lines, you don't need to set fixed width and height.
You can add some view (if your layout allows that) which will take the whole width of the screen. In this case you set leading and trailing to 0, and instead of setting fixed width (which varies on different screen sizes), you need to center that view horizontally inside the scrollView.
This will solve the horizontal dimension.
Instead of using constraints use the autoresizing feature. The controls resize depending on the way they were placed on screen and what the settings on the Autoresizing is.
Autoresizing

Dynamic Scrollview Swift

I have made a Button which generates TextFields and I want a ScrollView so if I'd add too much TextFields I could scroll and write to them. I also have a Button at the bottom of my view and I want this to be visible all the time. (I don't mind if only TextFields will be inside the ScrollView or the whole view except my bottom button).
You have to set the height constraint on your TextFields. Without that, the scrollView can't figure out its contentSize. After you set the heights, the scrollView will adapt accordingly.
Also, I would use a UIView and give a fix height for it and place the TextFields inside of the UIView.
You can also use scrollView.contentSize = CGSize(width: expectedWidth, height: expectedHeight)
These steps help you to design screen according to your requirement :
Drag a ScrollView and provide height and width accordingly
Decrease height of ScrollView by height of Button which you always want to show on bottom of page
i.e- Button should be outside of ScrollView not within the ScrollView
Adjust button size and height and put it below to ScrollView
Now set Constraint to each one . First of all select ScrollView provide its l
Left , Right , Top constraint .
Select ScrollView and by pressing Control + drag mouse to Button ( below of Scrollview) and set vertical spacing between ScrollView and Button
Select Button and provide Left , Right , Bottom Constraint.
Note - As AutoLayout works differently with ScrollView so always set constraint of all control within it from Order : Top To Bottom
Hope it helps you to fix this issue . Feel free to comment if still any confusion.
Thanks.

iOS: animate UICollectionView vertical expansion with constraints?

I've a view controller with a UICollectionView on top (using the default grid layout) followed by other controls below it. As I add / remove cells to / from the collection view, I want it to expand / contract in the vertical direction (so it has enough rows to show all of the cells and no more), and all controls below it to move down / up the screen accordingly. If you imagine how email app UIs work when you add / remove addresses, that's the effect i'm trying to implement. Achieving this effect using constraints, though, is eluding me. Any help would greatly, greatly appreciated!
What I did:
removed all constraints in IB
added a height constraint on the collection view and connected it to an IBOutlet
had IB add missing constraints
add an observer for the collection view's contentSize property
Now, when I add a cell to the collection view and tell it to reload, i'm notified when the contentSize changes and can set the constant for the height constraint to the contentSize height. Result: a collection view that resizes to fill it's content, with the other controls moving up / down as needed :-)

How can i change the height of UIPickerView so that i shows more rows (not scale)

One of my view controllers has 3 elements, a textfield, a custom picker and a button. I have a lot of screen space and I want to change the height of the picker so that it shows more rows.
I dont want to scale the picker or distort the elements in any way. Any ideas?
You can force a certain height with an autolayout constraint, but it won't give you what want. The pickers have three different heights they support (162, 180, 216) and they don't behave well when forced to a different height.

Resources