如何将AutoLayout和ScrollView很好地融合在一起

前因

据Nic说现场是大便,陈坤是马桶,所以能很好地融合在一起。

然而在日常的开发工作中,我们遇到AutoLayout和ScrollView,未必就能将它们很好地融合在一起。

今天Q群里也有朋友在踩AutoLayout和ScrollView的坑,过程中两者融合得不是很好,于是便在群里声泪俱下。为了让这类惨剧少那么一点天空蓝那么一点。

Demo

I'm Demo Address

后果

为了让这类惨剧少那么一点天空蓝那么一点。Demo地址已经在上面了,我分别用StoryBoard和代码(我使用的是PureLayout)示范了一个例子。

完成后的界面是这样的:


图是HOCC拿新秀大赛冠军时候梅姐颁奖的现场
这首歌很好听,欢迎听XD

其中代码的实现也很简单,一并附上。示例中将写约束的方法写在Controller里并不是最佳实践,实际应用时请自行封装(逃(并非凑字数XD

import UIKit

class HOCCCodeController: UIViewController {

    var didSetupConstraints = false

    let scrollView = UIScrollView.newAutoLayoutView()
    
    let container = UIView.newAutoLayoutView()
    
    // MARK: Container Subviews
    let imageView: UIImageView = {
        let imageView = UIImageView.newAutoLayoutView()
        imageView.contentMode = .ScaleAspectFit
        imageView.image = UIImage(named: "hocc")
        return imageView
    }()
    
    ...     
    ...
    

// MARK: LifeCircle
extension HOCCCodeController {
    override func loadView() {
        // Add Subviews
        view = UIView()
        view.backgroundColor = .whiteColor()
        view.addSubview(scrollView)
        
        scrollView.addSubview(container)
        
        let subviews = [imageView, songNameLabel, singerLabel, contentView, lyricLabel]
        for subview in subviews {
            container.addSubview(subview)
        }
        
        contentView.addSubview(lyricistLabel)
        contentView.addSubview(composerLabel)
        
        // Trigger
        view.setNeedsUpdateConstraints()
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        title = "Code"
    }
}

// MARK: UpdateViewConstraints
extension HOCCCodeController {
    override func updateViewConstraints() {
        if !didSetupConstraints {
            
            // 1. Setup ScrollView constraints
            scrollView.autoPinEdgesToSuperviewEdgesWithInsets(UIEdgeInsetsZero)
            
            // 2. Setup Container constraints
            container.autoPinEdgesToSuperviewEdgesWithInsets(UIEdgeInsetsZero)
                /* It is the Key */
            container.autoMatchDimension(.Width, toDimension: .Width, ofView: scrollView)
            
            imageView.autoAlignAxisToSuperviewAxis(.Vertical)
            imageView.autoPinEdgeToSuperviewEdge(.Top, withInset: 30)
            imageView.autoMatchDimension(.Width, toDimension: .Width, ofView: container, withMultiplier: 0.7)
            if let image = imageView.image {
                let ratio = image.size.height / image.size.width
                imageView.autoMatchDimension(.Height, toDimension: .Width, ofView: imageView, withMultiplier: ratio)
            }
            
            let views = [songNameLabel, singerLabel, contentView, lyricLabel]
            
            var previousView: UIView?
            for view in views {
                view.autoAlignAxisToSuperviewAxis(.Vertical)
                if let previousView = previousView {
                    view.autoPinEdge(.Top, toEdge: .Bottom, ofView: previousView, withOffset: 15)
                } else {
                    view.autoPinEdge(.Top, toEdge: .Bottom, ofView: imageView, withOffset: 30)
                }
                previousView = view
            }

            lyricLabel.autoSetDimension(.Width, toSize: 230)
            lyricLabel.autoPinEdgeToSuperviewEdge(.Bottom, withInset: 80)
            
            // 3. Setup ContentView constraints
            lyricistLabel.autoPinEdgesToSuperviewEdgesWithInsets(UIEdgeInsetsZero, excludingEdge: .Right)
            composerLabel.autoPinEdge(.Leading, toEdge: .Trailing, ofView: lyricistLabel, withOffset: 50)
            composerLabel.autoAlignAxis(.Horizontal, toSameAxisOfView: lyricistLabel)
            composerLabel.autoPinEdgeToSuperviewEdge(.Trailing)
            
            didSetupConstraints = true
        }
        super.updateViewConstraints()
    }
}

总结

如果有一天,你要实现类似的界面,你又刚好想用ScrollView和AutoLayout去实现。
直接上方法:

  • 添加scrollView以及其子视图
    • 父视图.addSubview(scrollView)
    • scrollView.addSubview(container) -- container为一个占位的view
    • container.addSubviews(真·想要展示的views)
  • 拉(或撸)约束
    • scrollView.四边 黏住 父视图.四边
    • container.四边 黏住 scrollView.四边
    • container.width = scrollView.width (如要实现横向滚动,则是container.height = scrollView.height)
    • container.subviews(真·想要展示的views)尽情布局,只需记住一点,拉(或撸)出来的约束要能确定出container的高度(横向滚动则为宽度)
      最直白的布局则像我的Demo中一样:"V:|-[imageView]-[label]-[label]--[contentView]-[label]-|",从上往下一个黏住一个,这样就可以确保高度可以算出来。

补充

对于用代码创建view的朋友,需要记得设置这个东东:

 (scrollView以及其子视图).translatesAutoresizingMaskIntoConstraints = false

再补充

平时本人如果要在ScrollView里使用AutoLayout,都会按照以上方法布局好scrollView与container,如果还有坑,那也基本只是普通的AutoLayout坑了(AutoLayout普通的坑?坑普通的AutoLayout?。。。)

至于这么做的原理是什么,该类文章网上已经有很多,譬如这篇:
这篇
这篇2:
还有我

再推荐一篇文章,里面提到如果scrollView计算出来的contentSize没有超出其本身的size,可在viewDidLayoutSubviews里将控件居中显示的方法:
我是文章

不知道以上内容有没有在你融合AutoLayout和ScrollView的过程中产生一点帮助呢:)有的话麻烦GitHub上star下。

谨以此文致敬尼古拉斯·谢

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,198评论 4 61
  • 我觉得交给,是相互的。 员工的交给是身,心,的交给。 身,以自我健康的体魄为公司做体力上的工作。 心,以自我思想上...
    冰咋吃阅读 226评论 0 3
  • 小裴寄语:你在关系中是习惯了表达吗?你有理有据的诉说自己的种种,却忽视了精妙的语言并不能代替真诚的泪水和美丽的笑...
    潼潼物语阅读 452评论 0 0
  • 五一将近,自驾游的朋友们一定少不了!很多朋友向教授反映:我的车油耗高,每个月的油钱挺多的,这也无形中增加了不少压力...
    小雨茅草屋阅读 165评论 0 1
  • 夜夜夜 北京夜生活分为两种:静吧听听歌喝喝酒或者比较燥的蹦蹦迪。建议你去去静吧感受下就好啊,或者选一个有露台的,看...
    黎芭啦阅读 505评论 0 0