Sizing a UICollectionView to fit its contents within an AutoLayout View - ios

I have a UIView that I'm using as a footer for a UITableView. Within this UIView Is a UIWebView and UICollectionView. They are laid out as follows:
H:|[webview]|
H:|[collection]|
V:|[webview]-8-[collection(10#1)]|
This is being done via IB but this indicates the constraints that are in place.
I'd like to have the webview sized to it's contents (I have that working) and the collection as well sized to it's contents. I'd like the overall UIView they are contained in sized to fit all of this without scrolling as it's contained in a tableview which handles it's own scrolling.
I can not seem to get the collection to size itself larger than the 10 points that the constraint has. In addition the constraint system demands that something have a height.
How do I get to my goal of a view sized to fit both subviews which are in turn sized to fit their contents? I have tried adjusting the frame on the collectionview in the sizeToFit method for the overall view but I believe the constraint system is undoing that.

Collection views have no intrinsic content size associated with them. There is currently no way to achieve what you are looking for.
If you feel that this is something that would be useful to have, please file an enhancement request at http://bugreport.apple.com (log in with your developer account).

Related

UIStackView losing constraints when using custom XIBs

I am attempting to dynamically add views to a UIStackView.
I have a XIB with a full screen UIStackView within. I then have some views - labels and text field/ text views.
The idea here is that I have a class that manages these individual views and that class has it's own XIB file for the layout.
So the class that handles the outermost UIStackView will grab the layout from the individual classes and add it as a subview to the UIStackView.
When attempting to do this, the view appears squashed and not in keeping with the original constraints.
I could offer code but I will explain the constraints.
The outer UIStackView is just stretched as full screen with distribution fill and equal spacing.
An example of an 'inner' view would be a label. I have an individual XIB file that holds a UIView and then a Label within that. The label is pinned to the center of the UIView.
I believe the issue lies with adding custom XIBs to an existing UIStackView. I'm not sure how much more information I give/ is required but I suspect there is something I am missing that someone may spot straight away from the information given.
Any help is appreciated. Thanks.
The UIStackView does exactly what you are asking it to do. If the constraints are setup so the UIStackView fills in the entire screen, then you need to provide content, that when it is equally divided on the screen, has the expected layout, each of the view inserted will be resized to be equally sized.
If you do not know how many views are going to be used, but you need each of your views to take their respective aspect ratio on the screen, then you can use the "Fill Proportionally" option. The UIStackView will add the height of every view, then divide its own height and distribute it proportionally to each view. If you add too many views this too will start to squash your views.
To avoid these kinds of issues, you may wish to put your UIStackView inside of a UIScrollView. You then setup a constraint with very very low priority to set the UIStackView height to 0, as you insert your views the UIStackView will automatically be resized to fit in the different views.
Good luck!

Setting UICollectionViewCellSize at runtime

I have a stack view that contains three UICollectionViews, set up to give each of them equal vertical space. That stack view is set to be the height of half of the display, so that it uses more space on larger devices. This has been set up in Interface Builder.
So, I need to set the cell size of the UICollectionView at runtime, since until we are running, I don't know what the actual size of the cells will be. I want them to be square, so I just want to take into account the height of the UICollectionView, subtract out the top and bottom section insets, and set the itemSize to the resulting size.
I attempt to do this in viewDidLayoutSubviews, since by then I figure that the initial heights of the collection views have been set. However, they appear to be set to 1000x1000 (even though they are a much more reasonable size in the storyboard), and so I compute a cell size based on a collection view height of 1000. This is too large, but I figure that I'll get called again and get another chance to recompute it. And I do, but not before UICollectionView complains loudly that the itemSize is incorrect (ie. too large to fit in the collectionView, which now has the "correct" size.)
What is the best way to get the behavior I'm looking for without the warnings from UICollectionView? Setting the collection view item size at runtime based on the eventual size of the UICollectionView is something I've struggled with in the past, and there never seems to be the "right" time to set the itemSize. I don't want to dynamically return it, if only because it's not something that changes during the life of the program. There just seems to be some inconsistencies that occur when laying out the views initially.
It seems odd to me that the collection view comes in with an initial size of 1000.0 by 1000.0, but I'm not sure how or why to fix that - perhaps it has something to do with being embedded in a stack view?
Edited to add: It is almost certainly the UIStackView that is causing the layout issues. I created a dummy project to test the size of a UICollectionView when it is the top level view vs embedded in a UIStackView. If it is not embedded, when viewDidLayoutSubviews is called, it has been properly sized to fit the bounds of its superview. However, if it is inside of a UIStackView, it stays at the default size of 1000x1000.
For now, I am working around this problem by adding the following code in viewDidLayoutSubviews:
if collectionView.bounds.size.width > view.bounds.size.width {
view.layoutIfNeeded()
}
Where collectionView is inside a UIStackView and view is the main view of the UIViewController. This allows all subsequent calculations based on the size of the view to be correct, and hopefully will not get called if the UIStackView behavior ever gets fixed.
Similar discussions here and here. Interesting point that in XCode8, the new default is to not save sizes of views in the XIB file, but instead bring everything in with an initial size of 1000x1000, to be resolved during the first layout pass. Except for UIStackViews, I guess.
Are you having sizeForItemAtIndexPath() return itemSize? I have found that implementing that function is the only reliable way to size a UICollectionViewCell dynamically at runtime.

sizeThatFits on view with constraints (auto layout view inside frame layout view)

View A layouts it's subviews by calculating and setting their frames in layoutSubviews.
View B uses constraints to layout it's subviews.
View A contains a number of View B instances.
View A needs to know the size of its subviews when it layouts them, so it calls sizeThatFits: on them.
View B's height depends on it's width. In my example it has a multiline UILabel inside it.
How should B implement it's sizeThatFits: so it returns the correct size taking both the size parameter passed to sizeThatFits: and the constraints in account?
What I've tried/considered so far:
systemLayoutSizeFittingSize: - doesn't work because it doesn't take into account an exact size passed to it. It can't be used to specify an exact width for e.g a view that grows vertically as it shrinks horizontally. It only gives either the most compressed size or the most expanded size.
intrinsicContentSize- this is used to pass information to Auto Layout as far as i understand from the docs. What we want is to get information from Auto Layout.
This is how i've reasoned so far. Please correct me if i've made a mistake. And please help me, i'd be very grateful if someone could point me to how this is done!
edit: Also, maybe there is some entirely different way to do this, without even using sizeThatFits:? The only requirement i have is that i can continue using frame layout in the "outer" view (View A) and auto layout in the "inner view" (View B).
edit2: Added a picture to illustrate the exact case i must solve
edit3: Forgot to mention, i need to support iOS 7.0
From the picture it would appear that a UIStackView might be a better approach for View A?
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIStackView_Class_Reference/
and then just constrain with a margin offset the bottom of the View A UIStackView to the bottom of the last View B
I would use Autolayout for the lot. You can still specify exact sizes and positions, you just do it with constraints instead of with frames.

UICollectionView - Last Row Not Showing

I have created a UICollectionView programmatically that will be displayed within a larger UIView. I've setup the delegate and datasource correctly, and it renders with no problem. However, when I increase the number of cells in the view to the point that scrolling is necessary to see the last row, I find that the UICollectionView is not able to scroll to the bottom. The last row is visible on the bounce, but is cut off when the bounce ends. Only about 10 pixels of a 75x75 pixel cell are visible, and there is no way past this.
I've tried increasing the content size, but that seems to have no affect. Is there something else I need to be doing to get the last row to display properly?
UPDATE: Perhaps a bit more details is in order...
My view setup looks like this: I have a paging-enabled UIScrollView that acts as my top-level UIView. Within this, I have created a series of UICollectionView objects. My UIScrollView is designed to scroll horizontally, while the UICollectionViews scroll vertically. I am accomplishing this using the RGMPagingScrollView library so I can benefit from reusable collection views to conserve memory.
As recommended, I took a look at implementing auto layout programmatically, but I am unclear as to exactly what to do. I've tried a couple different routes, but I end up with layout constraint errors. I also looked at the bounds and frame sizes for my collection views, and they appear to be correct. They end up being {{0, 0}, {320, 568}}, which I would expect for a full-screen collection view on my device.
I also took a look to see what the superview bounds/frame would be, but found that the superview for my UICollectionView was nil, which I did not expect since it is embedded within a UIScrollView. At this point, I'm at a loss as to what could be going on here. I've modified both the bounds and frame (shrinking the height by up to 200 px), thinking that might make a difference, but the behavior is the same.
This issue may also occur if minimumLineSpacing is being set for horizontal scrolling UICollectionViewFlowLayout.
Removing this worked for me.
You UICollectionView is probably going off screen. You can try autolayout to fix its size to its superview's size. You can also do it with autoresizing masks or overriding viewDidLayoutSubviews.

iOS Autolayout intrinsicContentSize = -1

When using autolayout, calling intrinsicContentSize seems to be the method to determine what CGSize is required to properly fit the views content.
However, this method is only supported for a limited number of existing UIViews.
Anytime that I make a custom view, even if it is something as simple as a UILabel inside of a container UIView, that containing view is unable to determine its intrinsicContentSize (returns -1).
I don't understand why the view is able to properly be displayed on the screen yet the view doesn't even know its own height...
The UILabel in a container view is a simple example but I'm dealing with slightly more complicated UIViews where there are maybe 15 views nested within eachother. In order to determine the size of the view which contains all of its subviews, I have to manually create my own intrinsicContentSize method and do very time consuming work where I have to sum up all the heights of the subviews plus add to that all of the constraints.
This process is terrible. It's very easy to miss out on a height somewhere by forgetting to add the height of one of the subviews or constraints. Also, the matter is further complicated by the fact that with dynamic subviews. For example, if the view has 2 columns of dynamic subviews, you need to manually find the height of the subviews+constraints for each column, compare these heights and return the larger of the two. Again, this is a simple example but often it's not so simple, causing many many migraines.
Back to what I was asking earlier. How can iOS display the view yet not even know how tall the view is? There must be some way to find out what that height is. Thanks for reading.
Here is an image to help visualize what I want.
Are all your subviews using auto-layout themselves? I mean that if your using auto-layout to place MyCompositeObject, is that composite object using constraints internally to place its many objects? I've found that if so, then the intrinsicContentSize will account for all the subviews, but if not, your UIView's intrinsic content size is going to end out returning something inaccurate and small.

Resources