I have a UICollectionView that is using flow layout and it is horizontally scrolling. In each cell, I have five subviews (labels and image views). I have an array of model objects, one object per cell, that contains the data to be displayed in each subview in each cell.
Some items can have more content than others (some labels will have more text), but I want all the cells to be the same height. I also want it so that the top of each subview is lined up with the tops of the same subview in the other cells. And if all model objects have a nil value for one of the fields, then the subviews for that field should be left out of each cell.
For example, I have the following image where it shows three cells and the subviews in each cell. For this example, all of the cells have data for subview 1, so it shows up in all of them. But only the first two cells have data for subview 2, so it only shows up in those cell. Likewise, subview 3 only shows up in the first cell because that is the only cell that has data for that subview. Now for subview 4, none of the cells have data for it, so it is completely skipped and there is no space taken up in any cell to render it. Instead, subview 5 is rendered in the place where subview 4 would normally have been rendered and it is only in the third cell because only that cell had data for it.
I also have dashed lines between the subviews showing that they are supposed to be lined up across cells.
Question
So I am just curious what the best way to do something like this is, given these requirements. Could this be done with AutoLayout or do I need to manually layout these subviews?
The only way I can think of doing this currently is to iterate through all of the model objects, try to figure out how tall each subview is and essentially keep track of the tallest height for each subview. And if no cell has data for a particular subview, the height of that one would be zero.
Next, I would turn those heights into computed offsets for each subview, where the computed offset would be the sum of the heights of the subviews above it and the spacing between each subview.
Then, I would try to lay these out manually, I suppose, with the frame of each subview being set to some width (not sure how to calculate the optimal width of each subview) and a vertical position equal to the computed offset of that subview. Or I guess I could do AutoLayout as well where the top constraint on a field is equal to the computed offset for each subview, but is that less efficient?
And then I guess the height of each cell would be just the computed offset of the last item plus its height plus the spacing to the bottom of the cell. So then I would take the max height of all the cells and set all of the cells to the same height.
But I was just curious if there was a better way of doing this or if there was anything that I was overlooking that could simplify things. I would also like this to be as efficient as possible as the collection view could have up to 100 items in it.
You will need to loop through your data, calculating the "max height" for each item.
I'd suggest putting your 5 "views" in a vertical stack view.
As you loop through your data, get the max height for each of the 5 views. Then, when you set your data in each cell, set the height constraint on each of the 5 views (or, set a view hidden if it is nil in all data items).
Then set your collection view's height to the necessary height to fit all the views plus spacing.
Related
This illustration shows what i'm trying to do:
The green list is the UITableView where it dynamically adjust it's height based on the number of items inside of it.
Underneath of the UITableView is a button that should follow the UITableView whenever it changes it's height size.
The UIButton should always be beneath the UITableView whatever the size of the UItableView.
I'm currently using autoresizing for UITableView
I have tried to use Autolayout but it seems i can't still find the answer.
i currently have no constraints in the layout.
This boils down to calculating the height of the table view that perfectly fits the cells. Basically you need to measure the size of every cell, then create a height constraint on the table view, and set its constant to the sum of the cells' heights.
Measuring the height of cells is tricky thought. If you only have a few cells (like in your illustrations), you can just instantiate all of them, keep them in an array and use systemLayoutSizeFittingSize to calculate their sizes. If you use multi-line labels, it is also important to set their preferredMaxLayoutWidth to appropriate values.
However, if you have only a few cells (and so cell reuse is not important), stack view is probably a better choice than table view. It's just too tricky to calculate the perfect height of a table view.
I have Viewcontroller in which I set the scrollview. In the scrollview I have tableview which is in the Container and just below the container I have an textview and some other UIViews when I set the tableview frame size equal to content size and tableview scroll property false so that only scrollview scrolls when the content size increase the textview goes up to tableview and I want that when tableview content increase then textview also goes down and parent view frame size increase and decrease as the content size
This look like a job for a UICollectionViewController. If you want to take a look here
you can get a better understanding of what they are and how to use them.
Basically you want to put each of those UITableViews, UILabels and the UIButtons in it's own cell(buttons need to be arranged) of the collectionView then the cell with the buttons try to use a stack view with the image. This will automatically set the different sections apart from each other. I know the idea of a collectionViewCell containing a tableView may seem weird but check this out
Another solution is to get crazy with constraints and autoLayout. I don't recommend this route because autoLayout will make decisions on it's own if the exact layout is not explicitly declared. Basically you'll set the constraints by pinning the tableView and labels to each other and the container they are in.
Checkout Apple's docs here
Good luck and let me know how it goes
I would recommend using a xib (if storyboards is your preference) or programmatically embed a tableview in the Scrollview Controller. Set a default cell height and then calculate the height of the Tableview by grabbing the amount of cells (which you'll normally get from .count in an array).
Afterwards, constrain the top, left, and right constraints of the tableview to its parent. You can imbed these in a scroll view for ease. Then if you want default text underneath it, all you have to do is constrain the top of the textview to the bottom of the tableview.
Do the cell for the button's. As the post above said, buttons might be best done in a CollectionView, so you can either create a default cell (xib again is useful) or use a prototype cell in storyboard. Collection views are the same principle as tableviews when it comes to populating data, they are just formatted differently and insets/spacing between cells.
This should help!
I have a Detail View Controller, which has different text lengths according to which cell you press in the Main View Controller, of which it segues to. How do I set the cell's height according to how much content is in it?
FYI, the cell consists of 3 labels and 1 image view. It is vertically set as UILabel, UIImageView, UILabel, UILabel.
I am using Swift.
This is what happens when I add the auto-layout constraints:
The way to do it is to use auto-layout. You need to set up vertical constraints from the top to the bottom of your cell:
Distance between the top edge of the cell and the top edge of the first label.
Distance from the bottom edge of the first label to the top edge of the image view.
Distance from the bottom edge of the image view to the top edge of the second label.
and so on ...
Auto-layout will take care of sizing the cell depending on the size of the content you put in your labels and the image view.
You should also remember to set the estimatedRowHeight property on your table view to some meaningful value. This will help the table view defer some of the calculations of the content size to when the user starts to scroll.
Also, set the rowHeight property on your table view to UITableViewAutomaticDimension.
There are 4 things you'll need to do:
Set your tableView:heightForRowAtIndexPath method to return UITableViewAutomaticDimension.
Set your tableView:estimatedHeightForRowAtIndexPath to return the average height of the cells. You can set it to something static like 100.
Set your Auto Layout constraints so that all subviews are tied to each other vertically, and the top-most and bottom-most views are tied up to the cell's contentView.
Set the label's preferredMaxLayoutWidth property either in code or in your Storyboard to be equal to the label's width.
To fix this, I added the sizeToFit() property to my label.
I have added UICollectionView.I have also added the Cell in the UICollectionView.The issue is cell always getting space from top of 74.On interface builder the y space column always shows the disabled value please tell how can i remove such issue?
Any given cell's position is determined by the collection view. You can adjust the cell's insets to increase spacing between cells and the scroll view's insets to position all of the cells in the scroll view, you can't directly manipulate a cell's position out of the box. When noting the position of your first cell you should also take into account whether your viewController has extend edges under top bars on and whether it has adjust scroll view insets on.
I have 3 labels in a UITableViewCell and have the labels set so they will wordwrap. If they word wrap the text goes into the next cell. How do I get AutoLayout to expand the cell based on the content without having to write code in the heightForRowAtIndex method? Isn't there a constraint I can use to automatically adjust the cell based on the contentView?
The cell looks fine if the text doesn't wrap in the label. Once it wraps that is when the problem occurs and I would like to have the cell resize to fit the content and have the same spacing between the bottom label and the bottom as there is between the top and top label.
Unfortunately no, you can't do this. A table view calculates its own total height first and has a fixed idea of the size of each cell as they load, it won't determine it's height from the outside in and it won't let layout constraints change the height of a cell.
If you think about how tables work, with cell reuse, then you couldn't really size the table from its cells without loading in every cell and adding it to the scrollview, and performing a layout pass on the whole thing. That would probably lead to quite poor performance.
You could experiment with populating a "free" cell (i.e a cell you've just instantiated, not added to a table) and laying it out for each row in your datasource when calculating heightForRow.
As you are loading the individual cells, after you fill the labels, but before you load the instance of the cell, check the label height.
Something like:
cell.frame.size.height
If the height is large enough that you know the label has wrapped to two lines, then increase the height of the cell you are about to load.