Вложенные контроллеры + представления, программно добавляющие ограничения, какое место? лучшая практика? - PullRequest
0 голосов
/ 11 сентября 2018

У меня есть несколько вопросов об ограничениях макета / лучших методах привязки ...

Резюме

  1. Я хотел бы узнать о наилучшей практикеи, возможно, некоторые объяснения, где создавать экземпляры ограничений, которые ссылаются на части родительского представления.Я вижу, что viewDidLoad не годится
  2. Какие альтернативы viewDidLoad имеют смысл
  3. Но также, куда его поместить или что установить, поэтому он вызывается при обновлении вращения (некоторые постоянные значения являются сложнымирасчеты, основанные на заданной ширине и количестве элементов, и мне нужно затем пересчитать)
  4. Нужно ли мне сохранять все свои ограничения в свойствах и ссылаться на них при обновлении или я могу вызывать self.view.leadingAnchor.constraint(equalTo: parentController.view.leadingAnchor,[..]) снова и снова

Мои настройки

В моем коде у меня есть вложенная иерархия, подобная этой

rootController
view
    menuViewController
    view
        menuOneViewController
        view
            UIButton1
            UIButton2
            ...
            UIButton6

Каждый уровень имеет ограничения вЧто касается привязок родительского уровня.

Я начал с добавления ограничений в метод viewDidLoad каждого контроллера.

menuViewController:

override func viewDidLoad() {
    super.viewDidLoad()

    self.menuOneViewController = MenuOneViewController()
    guard let menuOneViewController = self.menuOneViewController else { return }

    menuOneViewController.parentController = self

    self.view.addSubview(menuOneViewController.view)
}

menuOneViewController:

override func viewDidLoad() {
    super.viewDidLoad()
    guard let parentController = self.parentController else { return }
    let menuRowHeight = parentController.menuRowHeight

    self.view.heightAnchor.constraint(equalToConstant: menuRowHeight).isActive = true
    self.view.leadingAnchor.constraint(equalTo: parentController.view.leadingAnchor, constant: 0).isActive = true
    self.view.trailingAnchor.constraint(equalTo: parentController.view.trailingAnchor, constant: 0).isActive = true
    self.view.topAnchor.constraint(equalTo: parentController.view.topAnchor, constant: -(menuRowHeight)).isActive = true

И с этой настройкой я получил это сообщение:

'Unable to activate constraint with anchors 
<NSLayoutXAxisAnchor:0x600001199900 "UIView:0x7fd99e705b90.leading">
and <NSLayoutXAxisAnchor:0x600001199980 "UIView:0x7fd99e526bc0.leading"> 
because they have no common ancestor.  Does the constraint or its 
anchors reference items in different view hierarchies?  That's illegal.'

Myмысли и детали

И я думаю, я понял ... Я думаю, что иерархия еще не построена в методе viewDidLoad.Я предполагаю, что это происходит после viewDidLoad (), поэтому мне нужно было бы либо перенести такие ограничения в метод в дочернем элементе, который вызывается после viewDidLoad, либо я мог бы создать ограничения в родительском объекте после вызова addSubview.

Теперь, хотя последний способ будет работать наверняка, я все же хотел бы знать, в каком другом месте я бы его обычно ставил.А также я бы предпочел не беспокоить родительский контроллер представления логикой того, насколько большим должно быть представление дочерних контроллеров представления.

Так я бы тогда поместил такой код в updateViewConstraints?потому что если это так, то он, кажется, не вызывается при ротации, что было моей следующей заботой, или мне нужно установить некоторые флаги или сделать что-то еще.чтобы это срабатывало вручную.Если да, то какой метод ?, на каком уровне?

Кроме того, если этот метод будет вызван дважды, у меня возникнут проблемы из-за повторной генерации ограничений / дублирования конфликтующих ограничений?Какова лучшая практика в этом случае?

Также в этом отношении имеет ли какое-либо значение, если я использую NSLayoutConstraint(item: view, [..]).isActive = true против view.anchor.constraint(

Я впервые узнал о NSLayoutConstraint, затем одругие способы (якоря и язык визуального формата), и прямо сейчас у меня есть смесь anchor.constraints и NSLayoutConstraint, не уверенный, какой путь пойти.Но так как мне нравится мой чистый код, я бы хотел придерживаться одного способа.

Ответы [ 2 ]

0 голосов
/ 11 сентября 2018

Возможно, проблема в том, что viewDidLoad() вызывается до того, как представление menuOneViewController добавлено в представление menuViewController. Вот почему вы получаете ошибку ...because they have no common ancestor во время выполнения.

Большая проблема и решение заключается в том, что дочерний элемент не должен знать о представлении, в котором он ограничен. Это не только уменьшает возможность повторного использования компонента menuOneViewController, но также усложняет отладку в будущее. Ваши ограничения должны быть установлены в menuViewController после добавления представления mainOneViewController в качестве подпредставления. Кроме того, вы сэкономите немного места, используя NSLayoutConstraint.activate(_:), а также .constraint(equalTo:) без аргумента constant:.

Вот переопределение viewDidLoad() для menuViewController:

override func viewDidLoad() {
    super.viewDidLoad()

    self.menuOneViewController = MenuOneViewController()
    guard let menuOneViewController = self.menuOneViewController else { return }

    menuOneViewController.parentController = self

    self.view.addSubview(menuOneViewController.view)

    let subView = menuOneViewController.view
    NSLayoutConstraint.activate(constraints: [
        subView.heightAnchor.constraint(equalToConstant: menuRowHeight),
        subView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor)
        subView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor)
        subView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: -menuRowHeight)
    ]
}
0 голосов
/ 11 сентября 2018

Сначала чтобы добавить дочерний VC, вы должны

addChildViewController(child) 
view.addSubview(child.view)
child.view.translatesAutoresizingMaskIntoConstraints = false
// set constraints here
child.didMove(toParentViewController: self)

Секунда viewDidLoad - самое подходящее место для этого, что касается автоматического изменения макета вращения при повороте устройства, но если вы хотите что-то настроить, сделайте это как ограничение var и настройте его внутри

func viewWillTransition(to size: CGSize, 
               with coordinator: UIViewControllerTransitionCoordinator) {}

Также Apple рекомендует использовать якоря, поскольку он автоматически добавляет ограничение к соответствующему родительскому элементу, а также использует NSLayoutConstraint.activate([-,-,-,]), чтобы активировать множество ограничений, вместо того, чтобы ставить .active = true на каждое

...