Перевод ограничений в визуальном формате в 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
}

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, еще неизвестно).

Что дает ожидаемый результат:

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

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

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
Другие вопросы по тегам