Границы Subview равны нулю в layoutSubviews () - PullRequest
0 голосов
/ 05 июля 2019

Границы подпредставления подпредставления пользовательского UIView кажутся 0 в layoutSubviews(), поэтому использование границ в layoutSubviews() является проблемой.

Чтобы показать проблему, которую я поставилдемонстрация на GitHub: SOBoundsAreZero

Вот прямая ссылка на реализацию настраиваемого представления: DemoView.swift

Структура настраиваемого представления "DemoView "выглядит следующим образом:

DemoView
    firstLevelSubview
        secondLevelSubview

Эта структура создается программно с использованием Auto Layout.

Когда вызывается layoutSubviews(), представление и firstLevelSubview имеют ожидаемые границы, ноГраницы secondLevelSubview равны 0.

Я ожидал, что все подпредставления, использующие Auto Layout, будут иметь правильные границы, по крайней мере, при последнем вызове layoutSubviews.

Структура является абстракцией реального случая.,Чтобы избежать этой проблемы, secondLevelSubview может быть добавлено в качестве первого уровня подвиду DemoView.Тем не менее, это то, что в реальном случае неосуществимо.

Я чувствую, что здесь упускаю что-то простое, даже если это ожидаемое поведение.

Ответы [ 3 ]

2 голосов
/ 05 июля 2019

Мне удалось исправить это с помощью вызова secondLevelSubview.layoutIfNeeded() в layoutSubviews().

override func layoutSubviews() {
    super.layoutSubviews()

    secondLevelSubview.layoutIfNeeded()

    firstLevelSubview.layer.cornerRadius = bounds.width / 2
    secondLevelSubview.layer.cornerRadius = secondLevelSubview.bounds.width / 2

    print("bounds.width: \(bounds.width), contentView.bounds.width: \(firstLevelSubview.bounds.width), backgroundView.bounds.width: \(secondLevelSubview.bounds.width)")
}

Описание layoutIfNeeded():

Используйте этот метод, чтобы заставить представление обновить свое расположение немедленно.При использовании автоматической компоновки механизм компоновки обновляет положение представлений по мере необходимости для удовлетворения изменений в ограничениях.Используя представление, которое получает сообщение в качестве корневого представления, этот метод размещает поддерево представления, начиная с корневого.Если обновления макета не ожидают, этот метод завершается без изменения макета или вызова каких-либо обратных вызовов, связанных с макетом.

Таким образом, по существу, у вас возникла проблема с упорядочением.Подвид запланирован для макета, и Auto Layout доберется до него, но он еще этого не сделал.Позвонив по номеру layoutIfNeeded(), вы говорите Auto Layout о немедленном выполнении отложенного макета, чтобы можно было получить обновленную информацию о кадре.

Примечание: Вы также можете простопозвоните self.layoutIfNeeded(), и это будет макет DemoView и все его подпредставления.Это было бы полезно, если у вас было много таких подпредставлений и вам не нужно было вызывать layoutIfNeeded() для каждого из них.

0 голосов
/ 05 июля 2019

Вы можете получить желаемый результат, если попытаетесь получить доступ к UIView.bounds в главной очереди.

override func layoutSubviews() {
    super.layoutSubviews()

    DispatchQueue.main.async {
        self.firstLevelSubview.layer.cornerRadius = self.bounds.width / 2
        self.secondLevelSubview.layer.cornerRadius = self.secondLevelSubview.bounds.width / 2
        print("bounds.width: \(self.bounds.width), contentView.bounds.width: \(self.firstLevelSubview.bounds.width), backgroundView.bounds.width: \(self.secondLevelSubview.bounds.width)")
    }
}

//print:
//bounds.width: 64.0, contentView.bounds.width: 64.0, backgroundView.bounds.width: 54.0

Но почему?Так что здесь я копаю в вашей проблеме.Вот иерархия вызывающих представлений вашего DemoView метода layoutSubviews

DemoView
|
|___layoutSubviews
    |
    |___firstLevelSubview
        |
        |___layoutSubviews //Here you are trying to access bounds of second view which is still need to be resize or achieve it's bound
            |
            |___ secondLevelSubview
                | 
                |___layoutSubviews

И поэтому в этом случае основная очередь поможет вам получить действительные границы любых UIView.subView.

В другом случае я сделал это, я пытаюсь добавить secondLevelSubview к самому DemoView с ограничениями firstLevelSubview, как показано ниже:

fileprivate func setupViews() {
    backgroundColor = UIColor.clear

    firstLevelSubview.translatesAutoresizingMaskIntoConstraints = false
    firstLevelSubview.layer.masksToBounds = true
    firstLevelSubview.backgroundColor = UIColor.green
    addSubview(firstLevelSubview)
    firstLevelSubview.topAnchor.constraint(equalTo: topAnchor).isActive = true
    firstLevelSubview.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
    firstLevelSubview.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
    firstLevelSubview.leftAnchor.constraint(equalTo: leftAnchor).isActive = true

    secondLevelSubview.translatesAutoresizingMaskIntoConstraints = false
    secondLevelSubview.layer.masksToBounds = true
    secondLevelSubview.backgroundColor = UIColor.magenta
//  firstLevelSubview.addSubview(secondLevelSubview)
    addSubview(secondLevelSubview) //Here
    secondLevelSubview.widthAnchor.constraint(equalTo: firstLevelSubview.widthAnchor, multiplier: 0.84).isActive = true
    secondLevelSubview.heightAnchor.constraint(equalTo: firstLevelSubview.heightAnchor, multiplier: 0.84).isActive = true
    secondLevelSubview.centerXAnchor.constraint(equalTo: firstLevelSubview.centerXAnchor).isActive = true
    secondLevelSubview.centerYAnchor.constraint(equalTo: firstLevelSubview.centerYAnchor).isActive = true
}

И иерархия следует следующим образом:

DemoView
    |
    |___layoutSubviews
        |
        |___firstLevelSubview
            |
            |___layoutSubviews
            |
            |___secondLevelSubview
            |
            |___layoutSubviews

Итак, в приведенном выше случае, если вы попытаетесь напечатать UIView.bounds без какой-либо основной очереди, вы получите желаемый результат.

Дайте мне знать, это поможет вам понять иерархию.

0 голосов
/ 05 июля 2019

Во-первых, ваше представление интерпретируется без проблем, потому что у вас есть инициализатор, основанный на 'CGRect', поэтому он получает свое измерение!Во-вторых, Ваше первое подпредставление инициализируется после того, как его родительское представление получило свои размеры.Размеры первого подпредставления зависят от размеров родительского вида, которые готовы.Так что нет проблем здесь, как вы упомянули.Но в то время как ваше первое подпредставление пытается получить его измерения, ваше второе подпредставление также начинает получать свои измерения на основе первого подпредставления, которое еще не готово, поэтому оно ничего не получило.Я предлагаю сделать разные функции для каждого подпредставления.Сделайте ваш объект из класса представления с его инициализатором фрейма в вашем контроллере представления.В 'viewDidLoad' вызовите первую функцию подпредставления, а в 'viewDidLoadSubView' вызовите вторую функцию подпредставления.Надеюсь, поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...