I have UIScrollView
which covers the whole screen, and inside it I have 3 UIView
objects side-by-side, managed with paging. I'd like to make the right constraints so it would fit iPhone 6 as well.
It looks like this when dragging:
The constraints of the UIScrollView
are working well, but how do I arrange the constraints of the views inside the UIScrollView
?
thanks in Advance!!
I had a requirement to created tutorial pages by adding views to ScrollView and I ended up in issues in arranging the views properly. I was creating Views from xib using loadNibNamed
and I was not getting the correct frame when I was running the app in iPhone6 and 6plus, the frame of subviews were coming as 320 always. So I tried the pure autolayout. Since I was having a lot of views to be added, I used an array of views and then created a VFL string and then added to scrollview and it worked. Here is the sample that I created, this did the work for me
ScrollViewAutolayout
/*!
Create an array of views that we need to load
@param nil
@result creates array of views and adds it to scrollview
*/
-(void)setUpViews
{
[viewsDict setObject:contentScrollView forKey:@"parent"];
int count = 20;//Lets layout 20 views
for (int i=0; i<=count; i++) {
// I am loading the view from xib.
ContentView *contenView = [[NSBundle mainBundle] loadNibNamed:@"ContentView" owner:self options:nil][0];
contenView.translatesAutoresizingMaskIntoConstraints = NO;
// Layout the text and color
[contenView layoutTheLabel];
[contentScrollView addSubview:contenView];
[viewsArray addObject:contenView];
}
}
/*!
Method to layout the childviews in the scrollview.
@param nil
@result layout the child views
*/
-(void)layoutViews
{
NSMutableString *horizontalString = [NSMutableString string];
// Keep the start of the horizontal constraint
[horizontalString appendString:@"H:|"];
for (int i=0; i<viewsArray.count; i++) {
// Here I am providing the index of the array as the view name key in the dictionary
[viewsDict setObject:viewsArray[i] forKey:[NSString stringWithFormat:@"v%d",i]];
// Since we are having only one view vertically, then we need to add the constraint now itself. Since we need to have fullscreen, we are giving height equal to the superview.
NSString *verticalString = [NSString stringWithFormat:@"V:|[%@(==parent)]|", [NSString stringWithFormat:@"v%d",i]];
// add the constraint
[contentScrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:verticalString options:0 metrics:nil views:viewsDict]];
// Since we need to horizontally arrange, we construct a string, with all the views in array looped and here also we have fullwidth of superview.
[horizontalString appendString:[NSString stringWithFormat:@"[%@(==parent)]", [NSString stringWithFormat:@"v%d",i]]];
}
// Close the string with the parent
[horizontalString appendString:@"|"];
// apply the constraint
[contentScrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:horizontalString options:0 metrics:nil views:viewsDict]];
}
UPDATE SWIFT 3 VERSION
Added a new repo with Swift 3 version of the above code.
ScrollViewAutoLayout
Your scroll view's subviews need two sets of constraints.
The first set is to dictate the scrolling behavior of the scroll view (i.e. what the scroll view’s contentSize
is). We use the scroll view’s contentLayoutGuide
for this. Note, unlike most constraints, this does not dictate the size of the subviews, but only the relationship between these subview and the scroll view's contentSize
.
The second set is the constraints for the size of the subviews. For this, we use the scroll view’s frameLayoutGuide
.
Thus, assuming that you have three subviews, a red, green, and blue, respectively, you could do:
// Set horizontal constraints relative to scroll view contentLayoutGuide
NSLayoutConstraint.activate([
redView.leadingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.leadingAnchor),
greenView.leadingAnchor.constraint(equalTo: redView.trailingAnchor),
blueView.leadingAnchor.constraint(equalTo: greenView.trailingAnchor),
blueView.trailingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor),
])
// set constraints that are common to all three subviews
for subview in [blueView, greenView, redView] {
NSLayoutConstraint.activate([
// Set vertical constraints to scroll view contentLayoutGuide
subview.topAnchor.constraint(equalTo: scrollView.contentLayoutGuide.topAnchor),
subview.bottomAnchor.constraint(equalTo: scrollView.contentLayoutGuide.bottomAnchor),
// Set width and height of subviews relative to scroll view's frame
subview.widthAnchor.constraint(equalTo: scrollView.frameLayoutGuide.widthAnchor),
subview.heightAnchor.constraint(equalTo: scrollView.frameLayoutGuide.heightAnchor),
])
}
The above assumes that you’ve got translatesAuthresizingMaskIntoConstraints
for these three subviews turned off. But hopefully the above illustrates the idea.
By the way, you can also do this all in IB (referencing the “content layout guide” and “frame layout guide”, just like above). I just did it programmatically here so you can see exactly what’s going on.
Prior to iOS 11, we didn’t have contentLayoutGuide
and frameLayoutGuide
. So when you set constraints between a scroll view and its subviews, it acted like contentLayoutGuide
(i.e. only affected the relationship of the contentSize
and the subviews) and to set the actual size of the subviews, you had to reach to the scroll view’s superview:
// Set horizontal constraints relative to scroll view contentSize
NSLayoutConstraint.activate([
redView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
greenView.leadingAnchor.constraint(equalTo: redView.trailingAnchor),
blueView.leadingAnchor.constraint(equalTo: greenView.trailingAnchor),
blueView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
])
// set constraints that are common to all three subviews
for subview in [blueView, greenView, redView] {
NSLayoutConstraint.activate([
// Set vertical constraints to scroll view contentSize
subview.topAnchor.constraint(equalTo: scrollView.topAnchor),
subview.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
// Set width and height of subviews relative to superview
subview.widthAnchor.constraint(equalTo: view.widthAnchor),
subView.heightAnchor.constraint(equalTo: view.heightAnchor),
])
}
By the way, Apple discusses this behavior in Technical Note TN 2154.
But if targeting iOS 11 and later, use the more intuitive contentLayoutGuide
and frameLayoutGuide
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With