Autolayout scrollview with collectionview - ios

I have been struggling with this for a few days now, and I am looking to see if someone can help me with this AutoLayout problem.
In my iOS7 application, I have a UIView that has a UIScrollView and inside it a UIView(container) with some elements positioned. I have in there, a UIImageView, UITableView, UICollectionView, UITextView and a MapView. There is no height constraint on the UIScrollView and the container UIView. There are no height constraints on the UICollectionView and the UITextView.
What I want to accomplish is
The UITextview should expand to the content size as in all the text should appear without any vertical scrolling enabled for the UITextView.
The UICollectionView should always show all items and there should not be any scrolling enabled there as well.
Overall, I want a UIScrollView with items in it, that scale based on content. I have tried numerous things, but failed.
If anyone has pointers or suggestions on how to go about doing this, it would be very helpful.

OK, I would go about this in a completely different way.
First, get rid of the scrollView completely.
Just use a UICollectionView for this entire interface.
The UICollectionView can take a UIView for a section header. Make this UIView with your UITextView inside. You will need to manually calculate the correct height for your UITextView (and UIView).
Something like...
CGSize size = [theText sizeWithFont:<the font used> constrainedToSize:CGSizeMake(desiredWidth, CGFLOAT_MAX)];
Then just populate your collection view.
By doing this your collection view will control all the scrolling. Because you have set the textview to the correct size in the header you will have all the text there.

This is how I would go about it:
I assume your issue is with the height of the views, that affect the scrolling.
In the textViewDidChange: I would set the frame of the UITextView same as it's contentSize. When they are both the same, scrolling gets disabled.
After populating your UITableView and calling reloadData, I would set it's frame same as it's contentSize.
The mapView (MKMapView, I suppose) has the same frame throughout, I suppose. So you just use it's fixed height. If it changes height, you must store it's changing height each time it changes.
Once you have all the heights, add them up, and set the frame of the outer view same as then combined height of the inner view. Iterate this to all nested views, beginning from innermost views, and moving to outer views.
The catch here is, every time your content changes, the frames have to be resized. Hence changing the frames in textViewDidChange:, after reloadData, etc makes sense.
EDIT : One thing you might want to do first is, getting rid of redundant views. Your view hierarchy seems Rube Goldberg to me. The lesser views you have, the lesser work you will have to do.

Ok.. so I solved this problem by creating a IBOutlet for my NSLayoutConstraint on the UITextView in question.
I simply computed the height and then applied it on the constraint and it worked..
#Fogmeister - Your solution will also work, but it would require me to rewire a whole UI page.. Your approach is definitely a feasible one and shall keep in mind for future iOS apps..

Related

UITableView inside UIScrollView height error

Looks like iOS makes our life difficult for simple things that should be accomplished in seconds with auto layout.
Anyway I want to place a UITableView inside a UIScrollView (I know that tableview contains scrollview but this is not what I want). The View ierarchy is
OuterView
ScrollView
ContainerView
View 1
TableView
The expected result is to disable tableview functionality and the scrollview to scroll view1 and tableview together.
The problem is that the height of the scrollview is the devices height whatever the device orientation. If i change the tableview frame and content size to the one calculated by all rows it does not adapt to the scrollview content size. Why? What's auto layout doing then?
I have set constaints like Trailing-Leading-Top-Bottom spacing to all view as well as width and height equality between outer and container view.
I cannot find a SIMPLE tutorial for scenarios like this one.
The Apple documentation discourages embedding UITableView within a UIScrollView because it can cause unexpected behaviour:
You should not embed UIWebView or UITableView objects in UIScrollView objects. If you do so, unexpected behavior can result because touch events for the two objects can be mixed up and wrongly handled.
This is likely why you are having trouble finding a tutorial. Why do you want to embed a table inside a UIScrollView? It may be more effective to designate rows in the UITableView to act as the content you wish to put in the UIScrollView.

iOS: Getting height of views with programatically added constraints as only indicator

Hello there fellow iOS programmers. While creating an app I've ran into a problem I can't seem to find an answer to. Let's lay it out:
I'm creating a UIViewController with a UIScrollView as it's only child. Inside this view I have a UIView, and inside of this there is a list of UIViews with UILabels inside them. As you all know you need to specify a contentSize for a UIScrollView.
The problem is that the list needs to be dynamic with it's content, and I therefore have no way to know the views heights beforehand. I'm adding all views with constraints where the height is set to ">=0".
When I later try to set the height of the UIScrollView I need to either get the height of the UIView that the list is inside, or get the origin.y and height of the last view in the list. This of course needs to be ready by the time the view is displayed to the user.
I've currently tried view.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize), which returned 0; view.systemLayoutSizeFittingSize(UILayoutFittingExpandedSize), which returned 10000; and view.origin.y + view.frame.height, which also returns 0. It seems to me like the constraints haven't taken effect yet.
I've tried setting both constraints and UIScollView.contentSize from viewDidLoad(). I've also tried to set constraints in viewDidLoad and setting contentSize in viewWillAppear; this yielded the same results. Setting constraints in viewWillAppear and setting contentSize in viewDidLoad only caused a crash.
Bottom-line: When should I set up the UIScrollView.contentSize if I want to get view.height or similar methods to return a correct result, while at the same time be ready by the time the user sees the view?
Btw, I'm making this app in Swift, so answers in this language is preferred, but I'm able to translate from Objective-C to Swift; post in whatever suits you best.
Thank you! :)
You say:
As you all know you need to specify a contentSize for a UIScrollView.
No, as TN2154 says, the constraints between the scroll view and its subviews are "interpreted as the content size of the scroll view" (emphasis added). This is a boon, because you no longer have to mess around with contentSize if doing auto-layout. Just set the constraints for the scroll view's subviews and the content size takes care of itself. This leverages the labels' intrinsic size (and make sure that the label's numberOfLines to zero).
The only trick is that it sometimes cannot figure out the correct width of the labels (because the trailing constraint is to the scroll view's content size, it will sometimes make the scroll view a horizontally scrolling one). You can remedy this by either manually setting preferredMaxLayoutWidth or by setting a width constraint between the label and the scroll view's superview.
Personally, while I understand the inclination to add the UIView containers between the scroll view and the labels, I'd suggest losing them unless you need them for some other reason. IMHO, it simply complicates the constraints unnecessarily. It's hairy enough as it is. Obviously, if these containers bear other utility for you, then go ahead and keep them (and they'll work fine), but if you're doing this simply for the sake of the constraints, you might consider eliminating them.

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.

Resizing a UITextView in UIScrollView using Auto Layout issue

I really hate to ask here because I usually try to figure things out on my own. But on this one I've stuck for days and can't find a solution anywhere online.
I have a ScrollView containing multiple subviews. I've got an image view and two labels at the top with fixed heights. Then there is a UITextView and another ImageView (see pictures).
I add the text to the text view programmatically so it should have a dynamic height and the ImageView should move to the bottom so you can scroll. I don't want the TextView to be scrollable in itself but I want all the subviews to move as well.
I know I should be able to solve this issue using constraints. But I feel like I've tried everything and nothing worked yet. It worked when I disabled auto layout and moved the views manually. I'm wondering if there is a better way though.
As you can see I pinned the TextView to the ImageView above with a 1,000 priority and to the ImageView below with a 1,000 priority. The height constraint can not be deleted so I set it to the lowest possible priority. The ImageView on the bottom is pinned to the bottom of the superview with an absolute height. Its height constraint also has low priority. (I can post an image of the ImageView's constraints, if it helps)
I also tried adapting the frame programmatically but this is not working well in combination with auto layout. (If it helps I can of course post the code)
What am I doing wrong? Shall I just disable auto layout and do it manually? This seems unclean to me. Is it even possible to do?
I really appreciate your help :)
Greets,
Jan
Make sure the Scrolling Enabled attribute on the UITextView is unchecked in Interface Builder. I believe that the Auto Layout system takes that into account when it calculates the intrinsic content size.
If somebody is struggling with a similar problem: This is what I ended up doing:
Remove all subviews from the ScrollView in IB
Programmatically add a single UIView to the ScrollView.
Add all the views to the UIView as subviews (move them using setFrame)
Set the Frame of the UIView appropriately to the subviews
Set the ScrollView's contentSize to the size of the UIView.
A little more work but it finally works. This follows Apple's mixed approach guidelines that can be seen here (look for UIScrollView): http://developer.apple.com/library/ios/#releasenotes/General/RN-iOSSDK-6_0/index.html
The problem is the height setting. You somehow have to try to delete it. If you have added other constraints that are "sufficient", it should become deletable.
At the moment you have one user constraint for the height that is "Greater or equal" and an "Equals" constraint as well. Clearly, those are not working well together.
Maybe there is a conceptual error as well. The lower image view should not be fixed in position, so the distance to the lower image view will not be a "sufficient" constraint to let you delete the fixed height.
I think it should work if
the lower image view has a fixed height and
a fixed distance to the text view above, and
the text view has a minimum height as well as
a fixed distance to the image view above
(which should be fixed in relation to the superview).

Set UIView height based on inner view height using constraints

I have one UIView and inside that, N UILabels which are laid out relative to each other.
The containing UIView has a background color, I want to extend the UIView to be high enough to cover all labels inside it, so the background color is behind them all.
(I'm embedding them in a UIView so I can have the labels inset from the view edges.)
Is there away to make the UIView's height expand to fill its content? I can't figure it from the constraint options, it seems like its all relative to superviews.
Normally I'd just work out the frame sizes programatically in viewDidAppear but those are getting reset by the constraints system, AFAIK.
I think I actually worked it out though.
I had the labels height set manually from when I drag-dropped and resized it. Deleting the height constraint on the UILabel made it size to fit content, which causes its superview to resize too. At least I think that's the case, I'm new to constraints.
Will leave the question up since it will probably bite someone else too.

Resources