美文网首页iOS移动开发L的iOSIOS开发
如何将AutoLayout和ScrollView很好地融合在一起

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

作者: Fasa | 来源:发表于2015-09-01 18:19 被阅读2470次

    前因

    据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下。

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

    相关文章

      网友评论

      • zyg:感觉不是很直观 其实一句话可以概述的 呼呼 反正我看了半天
      • 许还真:有界面更好
        Fasa:@8f31bf8e34b4 其实整篇文章真正重要的是总结部分,本文不详解AutoLayout基本用法,所以不配图。有疑问可以直接看Demo,里面有storyBoard和代码两种示例
      • aa30758e486a:博主,如果把文章开头提到的坑重现下就更好了,能让读者明白的更透彻,谢谢!
        Fasa:@至逸 当时那位朋友出现的问题是因为没有在scrollView里加个contentView就直接布局,推荐做法就是如我文章中提到的做法,这个技巧简单,应用几次后就不会有问题的,还有问题也是AutoLayout其他问题了。

      本文标题:如何将AutoLayout和ScrollView很好地融合在一起

      本文链接:https://www.haomeiwen.com/subject/wbklcttx.html