使用NSLayoutConstraints使用Swift在ScrollView中垂直对齐动态视图

时间:2016-12-01 15:55:58

标签: ios swift nslayoutconstraint

我有一个应用程序,它具有动态生成的视图,并且每次加载视图时都可以不同。主视图包含一个设置为主视图边界的ScrollView。然后将子视图动态添加到ScrollView,但这些视图的高度将不同,并且它们可以随时更改高度(例如,用户单击视图的内容并更改)。我想使用布局约束来确保每个视图保持与其上方的视图对齐,并使用一些任意填充。见下图:

needed layout

现在所有填充值都设置为10(顶部,左侧和右侧)。我正在使用他们的框架手动设置这些子视图的位置,但是如果视图改变大小,这不起作用,所以我想改变它以使用NSLayoutConstraints,但我遇到了一些问题。

作为测试,我像以前一样设置子视图的框架,但后来我添加了约束:

// newView is created and its frame is initialized
self.scrlView?.addSubview(newView)
let constr = NSLayoutConstraint(item: newView, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: self.previousView, attribute: NSLayoutAttribute.Bottom, multiplier: 1, constant: 10)
NSLayoutConstraints.activateConstraints([constr])
newView.translatesAutoResizingMaskIntoConstraints = false
self.previousView = newView

但这些观点无处可见。我究竟做错了什么?所需要的只是确保每个视图的顶部在前一个视图下方对齐,并且无论视图高度如何,它们都保持这种状态。

此外,由于这些视图都已添加到滚动视图中,使用上面的布局约束如何设置滚动视图的正确内容大小?

1 个答案:

答案 0 :(得分:5)

所以你想要这样的东西:

demo

Technical Note TN2154: UIScrollView And Autolayout中说明了使用自动布局设置滚动视图的内容大小。总而言之,autolayout根据滚动视图及其后代视图之间的约束设置滚动视图的contentSize。这意味着:

  • 您需要在滚动视图及其后代视图之间创建约束,以便正确设置滚动视图contentSize

  • 您需要在切片(图片中的彩色视图)和主视图(滚动视图的超级视图)之间创建约束,以正确设置切片的宽度。从平铺到其封闭滚动视图的约束不能设置平铺的大小 - 仅滚动视图的contentSize

对于您在代码中创建的任何视图,如果您打算使用约束来控制其大小或位置,则还需要设置translatesAutoresizingMaskIntoConstraints = false。否则,自动调整掩码将干扰显式约束的行为。

以下是我制作演示的方法。我使用了UIButton子类,当点击时,将自己的高度切换到60到120点之间:

class HeightTogglingButton: UIButton {

    override init(frame: CGRect) {
        super.init(frame: frame)

        translatesAutoresizingMaskIntoConstraints = false
        heightConstraint = heightAnchor.constraint(equalToConstant: 60)
        heightConstraint.isActive = true
        addTarget(self, action: #selector(HeightTogglingButton.toggle(_:)), for: .touchUpInside)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    @IBAction func toggle(_ sender: Any) {
        UIView.animate(withDuration: 0.3) { 
            self.heightConstraint.constant = 180 - self.heightConstraint.constant
            self.window?.layoutIfNeeded()
        }
    }

    private(set) var heightConstraint: NSLayoutConstraint!
}

然后我在滚动视图中布置了十个这些按钮。我设置约束来控制每个按钮的宽度,以及每个按钮相对于其他按钮的布局。顶部和底部按钮被约束到滚动视图的顶部和底部,因此它们控制scrollView.contentSize.height。所有按钮都被约束到滚动视图的前沿和后沿,因此它们共同控制scrollView.contentSize.width

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let scrollView = UIScrollView()
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(scrollView)
        NSLayoutConstraint.activate([
            scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            scrollView.topAnchor.constraint(equalTo: view.topAnchor),
            scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor)])

        let margin: CGFloat = 10

        var priorAnchor = scrollView.topAnchor
        for i in 0 ..< 10 {
            let button = HeightTogglingButton()
            button.backgroundColor = UIColor(hue: CGFloat(i) / 10, saturation: 0.8, brightness: 0.3, alpha: 1)
            button.setTitle("Button \(i)", for: .normal)
            scrollView.addSubview(button)
            NSLayoutConstraint.activate([
                button.widthAnchor.constraint(equalTo: view.widthAnchor, constant: -2 * margin),
                button.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: margin),
                button.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: -margin),
                button.topAnchor.constraint(equalTo: priorAnchor, constant: margin)])
            priorAnchor = button.bottomAnchor
        }

        scrollView.bottomAnchor.constraint(equalTo: priorAnchor, constant: margin).isActive = true
    }

}