Использование NSLayoutConstraints для выравнивания динамических представлений по вертикали в ScrollView с помощью Swift

У меня есть приложение, которое имеет представление, которое динамически генерируется и может отличаться каждый раз при загрузке представления. Основное представление содержит 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

но нигде не было видно. Что я делаю не так? Все, что нужно, - это убедиться, что вершины каждого вида выровнены ниже предыдущего вида и остаются такими независимо от высоты вида.

кроме того, поскольку все эти представления добавляются в scrollview, используя ограничения макета выше, как установить правильный размер содержимого представления прокрутки?

1 ответов


так что вы хотите, что-то вроде этого:

demo

настройка размера содержимого вида прокрутки с автозапуском объясняется в техническое Примечание 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!
}

затем я выложил десять из этих кнопок в виде прокрутки. Я установил ограничения для управления шириной каждой кнопки и макетом каждой кнопки относительно других кнопок. Верхняя и нижняя кнопки ограничены верхней и нижней частью вида прокрутки, поэтому они управляют 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
    }

}