如何使用 AutoLayout 自动计算 ScrollView 的 contentSize

4,466
iOS version: 12.2
字数:900+
阅读时长:10 min

在日常开发中,令人看了就想素质三连的代码,莫过于手动计算各种数字的代码。如下图,看了真的是头皮发麻。

手动计算
那么,我们如何避免上面的问题,从而使我们的代码变得友好、可读性更高呢(主要为了保住我们为数不多的头发😭)?答案就是使用 AutoLayout 让系统自动计算数字。

下面,我们举一个实际的例子来一步一步的实现:使用 AutoLayout 自动计算 ScrollView 的 contentSize 。

先来说一下实现的大体步骤:

  • 设置 scrollView 的约束
    • 约束上下左右间距为 0
  • 创建一个 contentView ,设置 contentView 的约束
    • 上下左右间距为 0
    • 宽度和 scrollView 的父视图相等
    • 高度和 scrollView 的父视图相等,且优先级要低(这一步很重要,千万不要写错)
  • 将所有子控件添加到 contentView (而不是直接添加到 scrollView 上),并设置好子控件的内部约束

设置 scrollView 的约束

首先,给使用的 scrollView 添加完整的约束:

// 1. 设置 scrollView 的约束
view.addSubview(scrollView)
scrollView.translatesAutoresizingMaskIntoConstraints = false

NSLayoutConstraint.activate([
    scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
    scrollView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor),
    scrollView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
    scrollView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor)
    ])

创建 contentView,设置 contentView 的约束

设置完 scrollView,接下来设置 contentView 的约束:

// 2. 设置 contentView
let contentView = UIView(frame: .zero)
scrollView.addSubview(contentView)
contentView.translatesAutoresizingMaskIntoConstraints = false

let contentViewHeightAnchor = contentView.heightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.heightAnchor)
// 2.1 高度和 scrollView 的父视图相等,且`优先级要低`
contentViewHeightAnchor.priority = UILayoutPriority(rawValue: 1)

NSLayoutConstraint.activate([
    contentView.topAnchor.constraint(equalTo: scrollView.topAnchor),
    contentView.leftAnchor.constraint(equalTo: scrollView.leftAnchor),
    contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
    contentView.rightAnchor.constraint(equalTo: scrollView.rightAnchor),
    contentView.widthAnchor.constraint(equalTo: view.widthAnchor),
    contentViewHeightAnchor
    ])

添加子控件,设置内部约束

设置完 scrollView 和 contentView 的约束,只需设置内部子控件的约束,剩下的工作就可以交给 AutoLayout 去做了。

// 3.添加子控件,设置内部约束
titleLabel.text = "titleLabel"
contentView.addSubview(titleLabel)

titleLabel.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
    titleLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 10),
    titleLabel.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 10),
    titleLabel.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -10)
    ])

contentLabel.text = "将此处替换为你自己的字符串(超过一屏),即可看见滑动效果。"
contentLabel.numberOfLines = 0
contentView.addSubview(contentLabel)

contentLabel.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
    contentLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 10),
    contentLabel.leftAnchor.constraint(equalTo: titleLabel.leftAnchor),
    contentLabel.rightAnchor.constraint(equalTo: titleLabel.rightAnchor)
    ])

bottomLabel.text = "bottomLabel"
contentView.addSubview(bottomLabel)

bottomLabel.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
    bottomLabel.topAnchor.constraint(equalTo: contentLabel.bottomAnchor, constant: 10),
    bottomLabel.leftAnchor.constraint(equalTo: titleLabel.leftAnchor),
    bottomLabel.rightAnchor.constraint(equalTo: titleLabel.rightAnchor),
    bottomLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -10)
    ])

到这里,我们就大功告成了。妈妈再也不用担心我手动计算数字了😏 (成功保住了自己的头发)。

完整代码

class TestViewController: UIViewController {
    let scrollView = UIScrollView(frame: .zero)
    let titleLabel = UILabel(frame: .zero)
    let contentLabel = UILabel(frame: .zero)
    let bottomLabel = UILabel(frame: .zero)
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = UIColor.white
        setupSubviews()
    }
}

extension TestViewController {
    func setupSubviews() {
        // 1. 设置 scrollView 的约束
        view.addSubview(scrollView)
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        
        NSLayoutConstraint.activate([
            scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            scrollView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor),
            scrollView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
            scrollView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor)
            ])
        // 2. 设置 contentView
        let contentView = UIView(frame: .zero)
        scrollView.addSubview(contentView)
        
        contentView.translatesAutoresizingMaskIntoConstraints = false
        
        let contentViewHeightAnchor = contentView.heightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.heightAnchor)
        // 2.1 高度和 scrollView 的父视图相等,且`优先级要低`
        contentViewHeightAnchor.priority = UILayoutPriority(rawValue: 1)
        
        NSLayoutConstraint.activate([
            contentView.topAnchor.constraint(equalTo: scrollView.topAnchor),
            contentView.leftAnchor.constraint(equalTo: scrollView.leftAnchor),
            contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
            contentView.rightAnchor.constraint(equalTo: scrollView.rightAnchor),
            contentView.widthAnchor.constraint(equalTo: view.widthAnchor),
            contentViewHeightAnchor
            ])
        
        // 3.添加子控件,设置内部约束
        titleLabel.text = "titleLabel"
        contentView.addSubview(titleLabel)
        
        titleLabel.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            titleLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 10),
            titleLabel.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 10),
            titleLabel.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -10)
            ])
        
        contentLabel.text = "将此处替换为你自己的字符串(超过一屏),即可看见滑动效果"
        contentLabel.numberOfLines = 0
        contentView.addSubview(contentLabel)
        
        contentLabel.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            contentLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 10),
            contentLabel.leftAnchor.constraint(equalTo: titleLabel.leftAnchor),
            contentLabel.rightAnchor.constraint(equalTo: titleLabel.rightAnchor)
            ])
        
        bottomLabel.text = "bottomLabel"
        contentView.addSubview(bottomLabel)
        
        bottomLabel.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            bottomLabel.topAnchor.constraint(equalTo: contentLabel.bottomAnchor, constant: 10),
            bottomLabel.leftAnchor.constraint(equalTo: titleLabel.leftAnchor),
            bottomLabel.rightAnchor.constraint(equalTo: titleLabel.rightAnchor),
            bottomLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -10)
            ])
    }
}

效果图:

参考