#ios #swift #autolayout
#iOS #swift #автоматическое описание
Вопрос:
У меня есть пользовательский контроллер представления, используемый в качестве контроллера дочернего представления:
class ChildViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .green
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
calculatePreferredSize()
}
func calculatePreferredSize() {
let targetSize = CGSize(width: view.bounds.width,
height: UIView.layoutFittingCompressedSize.height)
preferredContentSize = view.systemLayoutSizeFitting(targetSize)
}
}
затем в главном контроллере представления у меня есть этот код:
class ViewController: UIViewController {
var container : UIView!
var childVC : ChildViewController!
var containerHeightConstraint: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .purple
// setup container to hold child vc
container = UIView()
container.backgroundColor = .systemPink
container.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(container)
container.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
container.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20).isActive = true
container.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20).isActive = true
containerHeightConstraint = NSLayoutConstraint()
containerHeightConstraint = container.heightAnchor.constraint(equalToConstant: 0)
containerHeightConstraint.isActive = true
// setup child vc
childVC = ChildViewController()
addChild(childVC)
container.addSubview(childVC.view)
childVC.view.frame = container.bounds
childVC.didMove(toParent: self)
// add contents into the child vc
let newView = UIView()
childVC.view.addSubview(newView)
newView.backgroundColor = .systemBlue
newView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
newView.topAnchor.constraint(equalTo: newView.superview!.topAnchor),
newView.leadingAnchor.constraint(equalTo: newView.superview!.leadingAnchor),
newView.trailingAnchor.constraint(equalTo: newView.superview!.trailingAnchor),
newView.heightAnchor.constraint(equalToConstant: 123),
])
}
override func preferredContentSizeDidChange(forChildContentContainer container: UIContentContainer) {
super.preferredContentSizeDidChange(forChildContentContainer: container)
if (container as? ChildViewController) != nil {
containerHeightConstraint.constant = container.preferredContentSize.height
}
}
}
Я пытаюсь динамически изменять размер container
представления в основном VC на основе вычисленной высоты дочернего элемента. Вызывается preferredContentSizeDidChange
метод, но при вычислении высоты дочернего VC (с использованием UIView.layoutFittingCompressedSize
) я всегда возвращаю 0. Несмотря на то, что я проверил рамку представления, добавленную в это представление, и она имеет правильную высоту кадра (в этом примере 123). Как показано в журнале вывода ниже:
(lldb) po view.subviews
▿ 1 element
- 0 : <UIView: 0x12251cd40; frame = (0 0; 350 123); layer = <CALayer: 0x6000007a0e60>>
(lldb) po UIView.layoutFittingCompressedSize
▿ (0.0, 0.0)
- width : 0.0
- height : 0.0
Ниже приведен скриншот из симулятора.
Я неправильно использую UIView.layoutFittingCompressedSize
? Как мне рассчитать высоту дочернего представления на основе его содержимого?
Комментарии:
1. Установлен ли в вашем дочернем контроллере представления соответствующие ограничения автозапуска? Может ли он действительно оценить правильную высоту в вашем
calculatePreferredSize
методе?systemLayoutSizeFitting
вычисляет минимальный размер, необходимый для удовлетворения всех ограничений. можете ли вы поставить точку останова и проверить, что вы возвращаете в концеcalculatePreferredSize
метода?2. Привет. Извините, но я на самом деле отвечаю на все это в вопросе. 1. Да, ограничения установлены для
newView
. 2. Нет, это невозможно — вот почему я задаю вопрос. 3. Да, я сделал это и включил вывод журнала. Рамка имеет правильные размеры (например, ширина 350 и высота 123), но покаsystemLayoutSizeFitting
не вычисляет ее правильно. Предположим, я вызываю его в неправильной последовательности или что-то в этом роде, потому что подвиды есть и даже видны на экране в правильном размере.3.
UIView.layoutFittingCompressedSize
всегда будет(0, 0)
, потому что это константа.4. @JurajBlahunka — спасибо, но я не думаю, что это правильно. Смотрите Документацию Apple по адресу developer.apple.com/documentation/uikit/uiview /… или это видео на отметке 37:30 developer.apple.com/videos/play/wwdc2018/220
5. Правильно,
UIView.layoutFittingCompressedSize
это руководство (целевой размер) дляsystemLayoutSizeFitting
метода. Метод не будет изменен. Что вам нужно проверить, так это возвращаемое значениеsystemLayoutSizeFitting
метода.
Ответ №1:
В Autolayout не удается вычислить newView
высоту содержимого, поскольку для решения уравнения отсутствуют ограничения по оси Y.
newView
определены только эти ограничения: верх, начало, конец и высота. В нем отсутствует нижнее ограничение:
newView.bottomAnchor.constraint(equalTo: newView.superview!.bottomAnchor).isActive = true
Полный набор ограничений будет выглядеть следующим образом:
NSLayoutConstraint.activate([
newView.topAnchor.constraint(equalTo: newView.superview!.topAnchor),
newView.leadingAnchor.constraint(equalTo: newView.superview!.leadingAnchor),
newView.trailingAnchor.constraint(equalTo: newView.superview!.trailingAnchor),
newView.heightAnchor.constraint(equalToConstant: 123),
newView.bottomAnchor.constraint(equalTo: newView.superview!.bottomAnchor)
])
Впоследствии, когда я помещаю точку останова preferredContentSizeDidChange
, я могу распечатать container.preferredContentSize.height
, что есть 123.0
.
РЕДАКТИРОВАТЬ Чтобы избежать нарушения ограничений, нам также необходимо использовать автозапуск для childVC.view
. Прямо сейчас он использует маску автоматического изменения размера, которая течет только сверху вниз и создает ограничения с приоритетом 1000.
childVC.view.frame = container.bounds
необходимо заменить на
childVC.view.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
childVC.view.topAnchor.constraint(equalTo: container.topAnchor),
childVC.view.leadingAnchor.constraint(equalTo: container.leadingAnchor),
childVC.view.trailingAnchor.constraint(equalTo: container.trailingAnchor),
childVC.view.bottomAnchor.constraint(equalTo: container.bottomAnchor)
])
и containerHeightConstraint
необходимо иметь пониженный приоритет для ограничения высоты 0, иначе система всегда будет находить ограничения неоднозначными — дочерний контроллер хочет иметь высоту 123 точки, но ограничение высоты контейнера по-прежнему равно 0, прежде чем мы вызовем preferredContentSizeDidChange
метод.
containerHeightConstraint.priority = .defaultLow
Комментарии:
1. Я явно чего-то не понимаю. Добавление этого 5-го ограничения приводит к неудовлетворительной ошибке автоматического ведения журнала макета, поскольку у представления уже установлена высота — 123, поэтому его нельзя привязать как к верхнему, так и к нижнему якорю родительского представления. Извините, но, возможно, я чего-то здесь не понимаю.
2. Добавление вышеизложенного вычисляет
preferredContentSizeDidChange
, но я думаю, что это неправильный подход, поскольку он приводит к тому, что автоматическая компоновка должна нарушать ограничение. и в этом случае это нарушает ограничение высоты, поэтому конечным результатом теперь является дочерний контроллер представления с высотой 0 при проверке иерархии представлений отладки в Xcode.3. Спасибо. Несмотря на то, что симулятор теперь отображается корректно, все еще существуют ошибки автоматического ограничения компоновки, потому что теперь у
newView
нас есть ограничения with на все края родительского представления, а также на высоту 123. Я также не совсем понимаю необходимость наличия ограничений 5 дляnewView
. Но я ценю вашу помощь.4. Глядя на это еще немного — я приведу вам другой пример. Если я перемещу
newView
код и ограничения в дочерний VC, а затем создам экземпляр этого контроллера представления в качестве основного контроллера представления, теперь синий вид заполняет весь экран, потому что мы привязали его ко всем краям. Вот почему я, хотя исходные ограничения, которые у меня были, — это правильный путь — сохраняет правильную высоту на уровне 123 и имеет синийnewView
цвет вверху. Но при внедрении в качестве дочернего VC мы вычисляем размер содержимого этого VC, используяsystemLayoutSizeFitting
иUIView.layoutFittingCompressedSize
5. Я думаю, что на данный момент лучше всего переформулировать ваш вопрос и, возможно, спросить об основных концепциях отдельно? Этот поток выходит за рамки исходного вопроса.