Here is my structure of views for this detail view (blogging application, I want to view the entire post which has dynamic height inside of a scrollview):
UIView
-UIScrollView
-SinglePostView (custom view)
-Title
-Subtitle
-Content
Normally to add a 'single post view' to a UIView I simply instantiate it, feed it my Post object and then add a single constraint that pins the width of the singlePostView to the superview, at which point things get laid out nicely. However when I try to add it to a scroll view, it doesn't show up nor does it scroll.
UIScrollView *scrollView = [[UIScrollView alloc] init];
[self.view addSubview:scrollView];
NOBSinglePostView *singlePost = [[NOBSinglePostView alloc] initWithPost:self.post];
[scrollView addSubview:singlePost];
singlePost.translatesAutoresizingMaskIntoConstraints = NO;
scrollView.translatesAutoresizingMaskIntoConstraints = NO;
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(scrollView,singlePost);
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[scrollView]|" options:0 metrics: 0 views:viewsDictionary]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[scrollView]|" options:0 metrics: 0 views:viewsDictionary]];
[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[singlePost]|" options:0 metrics: 0 views:viewsDictionary]];
in auto layout
frame of
scrollview
is decided by constraints betweenscrollview
andsuperview
ofscrollview
.contentSize
ofscrollview
is decided by constraints betweenscrollview
andsubview
ofscrollview
.you should set the size of
singlePostView
.ScrollView
calculatecontentSize
from it. (you need to add size constraints explicitly)In the structure you are presenting, using AutoLayout, the
contentSize
of yourUIScrollView
is driven by theintrinsicContentSize
of its subviews, here yourSinglePostView
.The problem is,
SinglePostView
being a subclass ofUIView
, itsintrinsicContentSize
is alwaysCGSizeZero
when considered by itself. What you need to do is make theintrinsicContentSize
of yourSinglePostView
depend on theintrinsicContentSize
of its subviews.Then, because the subviews of your
SinglePostView
areUILabels
, and because aUILabel
'sintrinsicContentSize
is the smallest size it needs to display its content, yourSinglePostView
'sintrinsicContentSize
will be equal to the sum of its subviewsintrinsicContentSizes
, that is the total size needed to display the content of all three of your labels.Here is how to do it.
Step 1: Removing all automatically set constraints
First, as you partially did, you need to remove all constraints automatically set by the system.
Assuming you don't have any constraints set in your storyboard or XIB (or you don't even have one of these), just do:
Now you have a clear slate and you can start setting your own constraints.
Step 2: Constraining the
scrollView
First, let's create, as you did, the views references dictionary for AutoLayout to use:
Then, also as you already did, let's constrain the scroll view to be the size of its superview:
Step 3: Constraining the
SinglePostView
to push thescrollView
'scontentSize
For this step to be clear, you have to understand that every constraints set between a
UIScrollView
and itssubviews
will actually change thecontentSize
of theUIScrollView
, not its actualbounds
. For Example, if you constrain aUIImageView
to the borders of its parentUIScrollView
and then put an image twice the size of theUIScrollView
inside theUIImageView
, your image won't get shrunk, its theUIImageView
that will take the size of its image and become scrollable inside theUIScrollView
.So here is what you have to set here:
First two constraints are pretty obvious. The third one, however, is here because, for your
UILabels
to display their content properly and still be readable, you will probably want them to be multilined and the scrolling to be vertical, not horizontal. That's why you set yourSinglePostView
'swidth
to be the same as yourscrollView
's. This way, you prevent yourscrollView
'scontentSize.width
to be anything more than itsbounds.width
.Step 4: Constraining your
UILabels
to "push" the bounds of yourSinglePostView
Fourth and final step, you now need to set constraints on your
SinglePostView
's subviews, so that it gets anintrinsicContentSize
from them.Here is how you do it (simplest implementation, no margins, one label after the other vertically):
And with that you should be done.
One last advice, you should definitely look into
UIStoryboard
to do these kinds of things. It's a lot simpler and more visual.Hope this helps,
P.S.: If you want, I can take some time and push a project using both
UIStoryboard
and theVisual Format Language
on Github. Just tell me if you would need one.Good luck.