MasonryMasonry
Guide
  • Objective-C API
  • Swift API
Examples
Changelog
  • English
  • 简体中文
GitHub
Guide
  • Objective-C API
  • Swift API
Examples
Changelog
  • English
  • 简体中文
GitHub
  • Examples

    • Common Layout Patterns
    • Animation with Constraints
    • ScrollView Layout

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)
}

:::

Edit this page on GitHub
Last Updated: 3/29/26, 12:09 PM
Contributors: samzhjiang
Prev
Animation with Constraints