ScrollView Layout
Laying out content inside a UIScrollView with Auto Layout can be tricky. Masonry makes it straightforward.
Basic ScrollView Setup
The key principle: the scroll view's content size is determined by the constraints of its content views.
::: code-group
UIScrollView *scrollView = [[UIScrollView alloc] init];
[self.view addSubview:scrollView];
// Pin scroll view to edges
[scrollView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.view);
}];
// Create a content view
UIView *contentView = [[UIView alloc] init];
[scrollView addSubview:contentView];
// Pin content view to scroll view edges AND set width
[contentView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(scrollView);
make.width.equalTo(scrollView); // Prevents horizontal scrolling
}];
let scrollView = UIScrollView()
view.addSubview(scrollView)
// Pin scroll view to edges
scrollView.mas.makeConstraints { make in
make.edges.equalTo(view)
}
// Create a content view
let contentView = UIView()
scrollView.addSubview(contentView)
// Pin content view to scroll view edges AND set width
contentView.mas.makeConstraints { make in
make.edges.equalTo(scrollView)
make.width.equalTo(scrollView) // Prevents horizontal scrolling
}
:::
Vertical Scrolling Content
Add content views inside the content view, and make sure the last view's bottom is pinned:
::: code-group
UIView *lastView = nil;
for (int i = 0; i < 20; i++) {
UIView *itemView = [[UIView alloc] init];
itemView.backgroundColor = (i % 2 == 0) ? [UIColor lightGrayColor] : [UIColor whiteColor];
[contentView addSubview:itemView];
[itemView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.equalTo(contentView);
make.height.mas_equalTo(80);
if (lastView) {
make.top.equalTo(lastView.mas_bottom);
} else {
make.top.equalTo(contentView);
}
}];
lastView = itemView;
}
// Pin the bottom of the last view to the content view
[lastView mas_makeConstraints:^(MASConstraintMaker *make) {
make.bottom.equalTo(contentView);
}];
var lastView: UIView?
for i in 0..<20 {
let itemView = UIView()
itemView.backgroundColor = (i % 2 == 0) ? .lightGray : .white
contentView.addSubview(itemView)
itemView.mas.makeConstraints { make in
make.left.right.equalTo(contentView)
make.height.equalTo(80)
if let last = lastView {
make.top.equalTo(last.mas.bottom)
} else {
make.top.equalTo(contentView)
}
}
lastView = itemView
}
// Pin the bottom of the last view to the content view
lastView?.mas.makeConstraints { make in
make.bottom.equalTo(contentView)
}
:::
Horizontal Scrolling
For horizontal scrolling, set the height equal to the scroll view instead of the width:
::: code-group
[contentView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(scrollView);
make.height.equalTo(scrollView); // Prevents vertical scrolling
}];
// Add horizontal items
UIView *lastItem = nil;
for (int i = 0; i < 10; i++) {
UIView *item = [[UIView alloc] init];
[contentView addSubview:item];
[item mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.bottom.equalTo(contentView);
make.width.mas_equalTo(120);
if (lastItem) {
make.left.equalTo(lastItem.mas_right).offset(10);
} else {
make.left.equalTo(contentView);
}
}];
lastItem = item;
}
[lastItem mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(contentView);
}];
contentView.mas.makeConstraints { make in
make.edges.equalTo(scrollView)
make.height.equalTo(scrollView) // Prevents vertical scrolling
}
// Add horizontal items
var lastItem: UIView?
for i in 0..<10 {
let item = UIView()
contentView.addSubview(item)
item.mas.makeConstraints { make in
make.top.bottom.equalTo(contentView)
make.width.equalTo(120)
if let last = lastItem {
make.left.equalTo(last.mas.right).offset(10)
} else {
make.left.equalTo(contentView)
}
}
lastItem = item
}
lastItem?.mas.makeConstraints { make in
make.right.equalTo(contentView)
}
:::
Important
Always ensure the content view's constraints form a complete chain from top to bottom (for vertical scrolling) or left to right (for horizontal scrolling). Missing the final constraint will result in zero content size.
ScrollView with Safe Area
For scroll views that should respect the safe area:
::: code-group
[scrollView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.view.mas_safeAreaLayoutGuideTop);
make.bottom.equalTo(self.view.mas_safeAreaLayoutGuideBottom);
make.left.right.equalTo(self.view);
}];
scrollView.mas.makeConstraints { make in
make.top.equalTo(view.mas.safeAreaLayoutGuideTop)
make.bottom.equalTo(view.mas.safeAreaLayoutGuideBottom)
make.left.right.equalTo(view)
}
:::