Преобразование ограничений в визуальный формат в API-интерфейсы NSLayoutAnchor

У меня есть следующее расширение, которое я получил от этот вопрос SO несколько месяцев назад, и я хотел бы перевести это на NSLayoutAnchor (или более подробные API-интерфейсы макета) по той простой причине, что визуальный формат не поддерживает указание руководства по макету безопасной области.

Расширение по ссылке вот такое:

extension UIView {

    /// Adds constraints to this `UIView` instances `superview` object to make sure this always has the same size as the superview.
    /// Please note that this has no effect if its `superview` is `nil` – add this `UIView` instance as a subview before calling this.
    func bindFrameToSuperviewBounds() {
        guard let superview = self.superview else {
            print("Error! `superview` was nil – call `addSubview(view: UIView)` before calling `bindFrameToSuperviewBounds()` to fix this.")
            return
        }

        self.translatesAutoresizingMaskIntoConstraints = false
        superview.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[subview]-0-|", options: .directionLeadingToTrailing, metrics: nil, views: ["subview": self]))
        superview.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[subview]-0-|", options: .directionLeadingToTrailing, metrics: nil, views: ["subview": self]))
    }
}

Моя иерархия представлений:

UINavigationController -> контроллер корневого представления: UIPageViewController -> Первая страница: UIViewController -> UIScrollView -> UIImageView

Когда я вызываю bindFrameToSuperviewBounds(), результат следующий:

введите здесь описание изображения

Что выглядит в основном хорошо. Проблема в том, что просмотр изображения находится под панелью навигации, чего я не хочу. Итак, чтобы исправить это, я попытался переписать bindFrameToSuperviewBounds() следующим образом:

guard let superview = self.superview else {
            fatalError("Attempting to bound to a non-existing superview.")
        }

translatesAutoresizingMaskIntoConstraints = false
self.topAnchor.constraint(equalTo: superview.safeAreaLayoutGuide.topAnchor).isActive = true
self.leadingAnchor.constraint(equalTo: superview.safeAreaLayoutGuide.leadingAnchor).isActive = true
self.bottomAnchor.constraint(equalTo: superview.safeAreaLayoutGuide.bottomAnchor).isActive = true
self.trailingAnchor.constraint(equalTo: superview.safeAreaLayoutGuide.trailingAnchor).isActive = true

Результат тогда такой:

введите здесь описание изображения

На этот раз он правильно не проходит под панелью навигации, но что ж... Вы можете видеть, что он растягивается, чего я не хочу.

Каков правильный способ перевести этот API визуального макета во что-то, что может учитывать руководство по макету безопасной зоны?

Для полноты ниже приведен код, который настраивает вид прокрутки и вид изображения:

override func viewDidLoad() {
    super.viewDidLoad()
    imageScrollView.addSubview(imageView)
    imageScrollView.layoutIfNeeded()
    imageView.bindFrameToSuperviewBounds()
    view.addSubview(imageScrollView)
    imageView.layoutIfNeeded()
    imageScrollView.backgroundColor = .white
    imageScrollView.bindFrameToSuperviewBounds()
    imageScrollView.delegate = self
}

person Andy Ibanez    schedule 21.10.2017    source источник


Ответы (2)


С кодом, предоставленным Fattie, я смог прийти к этому решению:

guard let superview = self.superview else {
    fatalError("Attempting to bound to a non-existing superview.")
}

translatesAutoresizingMaskIntoConstraints = false
self.topAnchor.constraint(equalTo: superview.safeAreaLayoutGuide.topAnchor).isActive = true
self.leadingAnchor.constraint(equalTo: superview.leadingAnchor).isActive = true
self.bottomAnchor.constraint(equalTo: superview.bottomAnchor).isActive = true
self.trailingAnchor.constraint(equalTo: superview.trailingAnchor).isActive = true

(Теперь, когда я только привязываю верхнюю часть к безопасному макету, это руководство. Создает ли это проблемы в iPhone X, еще неизвестно).

Что дает результат, который я ожидаю:

введите здесь описание изображения

person Andy Ibanez    schedule 22.10.2017

Честно говоря, я бы не стал заморачиваться с причудливыми «визуальными» вещами макета, они скоро исчезнут.

Это помогает?

extension UIView {

    // put this view "inside" it's superview, simply stretched all four ways
    // amazingly useful

    func bindEdgesToSuperview() {

        guard let s = superview else {
            preconditionFailure("`superview` nil in bindEdgesToSuperview")
        }

        translatesAutoresizingMaskIntoConstraints = false
        leadingAnchor.constraint(equalTo: s.leadingAnchor).isActive = true
        trailingAnchor.constraint(equalTo: s.trailingAnchor).isActive = true
        topAnchor.constraint(equalTo: s.topAnchor).isActive = true
        bottomAnchor.constraint(equalTo: s.bottomAnchor).isActive = true
    }

}

Просто используйте вот так..

    textOnTop = ..  UILabel or whatever
    userPhoto.addSubview(textOnTop)
    textOnTop.bindEdgesToSuperview()

легкий !

(PS - не забывайте, если это изображение: вы почти наверняка захотите установить AspectFit всегда и во всех случаях.)

Ручки для безопасного макета руководства похожи на ...

.safeAreaLayoutGuide
person Fattie    schedule 22.10.2017
comment
Вероятно, мне следовало упомянуть, что я не использую раскадровки и пишу свой код пользовательского интерфейса. Я видимо не правильно сформулировал свой вопрос. Ваш пример кода выполняет перевод, но он не принимает во внимание безопасные поля макета, поэтому результат такой же, как и на первой фотографии, которую я разместил. Кроме того, учитывая, что с этим вопросом могут столкнуться многие люди, вам обязательно следует решить все проблемы с помощью предоставленного мной примера кода. Ваш код и я по сути одинаковы, с той разницей, что я стараюсь использовать руководство по безопасному макету. - person Andy Ibanez; 22.10.2017
comment
Привет @AndyIbanez, да, именно это я и имел в виду. Я знаю, что вы делаете это в коде. Честно говоря, ›››НЕ ИСПОЛЬЗУЙТЕ‹‹‹ эту дурацкую штуку с визуальным языком. Это совершенно глупо, не работает и со дня на день будет объявлено устаревшим! - person Fattie; 22.10.2017
comment
О, извините, я не включил .safe .. etc в качестве примера одного из ограничений. Похоже, вы получили это ниже! - person Fattie; 22.10.2017
comment
Один последний момент! Во всех проектах со всеми командами для всех клиентов мы никогда не используем руководство по безопасности. Хе! Многие другие команды и проекты также принимают эту политику. Для тех, кто гуглит здесь, вы можете просто отключить мусор руководства по безопасной области в начальной раскадровке в инспекторе файлов, надеюсь, это кому-то поможет. - person Fattie; 22.10.2017