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

Eva*_*man 6 ios nslayoutconstraint swift

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

需要布局

现在所有填充值都设置为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
Run Code Online (Sandbox Code Playgroud)

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

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

rob*_*off 5

所以你想要这样的东西:

演示

使用autolayout设置滚动视图的内容大小在技术说明TN2154:UIScrollView和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!
}
Run Code Online (Sandbox Code Playgroud)

然后我在滚动视图中布置了十个这些按钮.我设置约束来控制每个按钮的宽度,以及每个按钮相对于其他按钮的布局.顶部和底部按钮被约束到滚动视图的顶部和底部,因此它们可以控制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
    }

}
Run Code Online (Sandbox Code Playgroud)