简体   繁体   English

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

[英]Using NSLayoutConstraints to align dynamic views vertically within a ScrollView using Swift

I have an application which has a view that is dynamically generated and can be different every time the view loads. 我有一个应用程序,它具有动态生成的视图,并且每次加载视图时都可以不同。 The main view contains a ScrollView set to the bounds of the main view. 主视图包含一个设置为主视图边界的ScrollView。 Subviews are then added dynamically to the ScrollView, but the heights of these views will not be the same and they can change heights at any time (eg. user clicks contents of view and it changes). 然后将子视图动态添加到ScrollView,但这些视图的高度将不同,并且它们可以随时更改高度(例如,用户单击视图的内容并更改)。 I want to use layout constraints to make sure each view stays aligned to the one above it with some arbitrary padding. 我想使用布局约束来确保每个视图保持与其上方的视图对齐,并使用一些任意填充。 See the image below: 见下图:

需要布局

Right now all padding values are set to 10 (top, left, and right). 现在所有填充值都设置为10(顶部,左侧和右侧)。 I am setting the positions of these subviews manually using their frames but this does not work if the views change size, so I want to change this to use the NSLayoutConstraints, but I am running into some issues. 我正在使用他们的框架手动设置这些子视图的位置,但是如果视图改变大小,这不起作用,所以我想改变它以使用NSLayoutConstraints,但我遇到了一些问题。

As a test I set the subview's frame like I did before, but then I added the constraint: 作为测试,我像之前一样设置子视图的框架,但后来我添加了约束:

// 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

But the views are nowhere to be seen. 但这些观点无处可见。 What am I doing wrong? 我究竟做错了什么? All that is needed is to make sure the tops of each view are aligned below the previous view and that they stay that way regardless of the view heights. 所需要的只是确保每个视图的顶部在前一个视图下方对齐,并且无论视图高度如何,它们都保持这种状态。

Also, since these views are all added to a scrollview, with using the layout constraints above how do I set the correct content size of the scroll view? 此外,由于这些视图都添加到滚动视图中,使用上面的布局约束如何设置滚动视图的正确内容大小?

So you want something like this: 所以你想要这样的东西:

演示

Setting the content size of the scroll view with autolayout is explained in Technical Note TN2154: UIScrollView And Autolayout . 使用autolayout设置滚动视图的内容大小在技术说明TN2154:UIScrollView和Autolayout中进行了说明 To summarize, autolayout sets the contentSize of the scroll view based on the constraints between the scroll view and its descendant views. 总而言之,autolayout根据滚动视图及其后代视图之间的约束设置滚动视图的contentSize This means: 这意味着:

  • You need to create constraints between the scroll view and its descendant views so the scroll view's contentSize will be set correctly. 您需要在滚动视图及其后代视图之间创建约束,以便正确设置滚动视图的contentSize

  • You need to create constraints between the tiles (the colored views in your picture) and the main view (which is the superview of the scroll view) to set the width of the tiles correctly. 您需要在切片(图片中的彩色视图)和主视图(滚动视图的超级视图)之间创建约束,以正确设置切片的宽度。 Constraints from a tile to its enclosing scroll view cannot set the size of the tile—only the contentSize of the scroll view. 从图块到其封闭滚动视图的约束不能设置图块的大小 - 仅滚动视图的contentSize

For any view you create in code, if you intend to use constraints to control its size or position, you also need to set translatesAutoresizingMaskIntoConstraints = false . 对于您在代码中创建的任何视图,如果您打算使用约束来控制其大小或位置,则还需要设置translatesAutoresizingMaskIntoConstraints = false Otherwise the autoresizing mask will interfere with the behavior of your explicit constraints. 否则,自动调整掩码将干扰显式约束的行为。

Here's how I made the demo. 以下是我制作演示的方法。 I used a UIButton subclass that, when tapped, toggles its own height between 60 and 120 points: 我使用了一个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!
}

Then I laid out ten of these buttons in a scroll view. 然后我在滚动视图中布置了十个这些按钮。 I set up constraints to control the width of each button, and the layout of each button relative to the other buttons. 我设置约束来控制每个按钮的宽度,以及每个按钮相对于其他按钮的布局。 The top and bottom buttons are constrained to the top and bottom of the scroll view, so they control scrollView.contentSize.height . 顶部和底部按钮约束在滚动视图的顶部和底部,因此它们控制scrollView.contentSize.height All buttons are constrained to the leading and trailing edges of the scroll view, so they all jointly control scrollView.contentSize.width . 所有按钮都被约束到滚动视图的前沿和后沿,因此它们共同控制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
    }

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM