Почему AutoLayout не соблюдает ограничения, если для целевого представления задан начальный кадр? - PullRequest
0 голосов
/ 15 марта 2020

У нас есть странный случай, когда наше представление выглядит не так, как ожидалось, и мы проследили его до AutoLayout и некоторой последовательности событий, которые мы не можем понять, почему это изменило бы вещи.

Чтобы упростить Например, вот некоторый тестовый код, который вы можете запустить непосредственно на игровой площадке Xcode. (Мы используем Xcode 11.3.1, если это имеет значение.)

Сначала мы определяем оператор InlineConfigure, например так ...

// 'InlineConfigure' operator
infix operator ~> : AssignmentPrecedence

@discardableResult
public func ~> <T>(item:T, _ configure:(T)->Void) -> T {
    configure(item)
    return item
}

Далее, чтобы проиллюстрировать, что это не так В случае, когда что-то освобождается, когда не ожидается, мы создаем подкласс UIView специально для регистрации этого ...

class SomeView : UIView {

    override init(frame: CGRect) {
        super.init(frame:frame)
        print("SomeView created")
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    deinit {
        print("SomeView deinitialized")
    }
}

С обоими из вышеперечисленных рассмотрим этот код. Он работает, как и ожидалось.

class TestViewController : UIViewController {

    let margin:CGFloat = 24

    override func loadView() {

        view = UIView()
        view.backgroundColor = .yellow

        SomeView()~>{

            $0.backgroundColor = .blue
            $0.translatesAutoresizingMaskIntoConstraints = false
            view.addSubview($0)

            NSLayoutConstraint.activate([
                $0.leftAnchor.constraint(equalTo: view.leftAnchor, constant: margin),
                $0.topAnchor.constraint(equalTo: view.topAnchor, constant: margin),
                $0.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -margin),
                $0.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -margin)
            ])
        }
    }
}

PlaygroundPage.current.liveView = TestViewController()

Если мы, однако, изменим строку, которая создает SomeView, на кадр, имеющий ненулевую область (например, ширину и высоту), то этот же код больше не работает, и вы вообще не видите синий вид.

override func loadView() {

    view = UIView()
    view.backgroundColor = .yellow

    SomeView(frame:CGRect(x: 0, y: 0, width: 20, height: 20))~>{

        $0.backgroundColor = .blue
        $0.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview($0)

        NSLayoutConstraint.activate([
            $0.leftAnchor.constraint(equalTo: view.leftAnchor, constant: margin),
            $0.topAnchor.constraint(equalTo: view.topAnchor, constant: margin),
            $0.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -margin),
            $0.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -margin)
        ])
    }
}

Установка рамки на нулевую область, и она снова работает ...

SomeView(frame:CGRect(x: 0, y: 0, width: 0, height: 20))~>{

    [...]

}

Но назад в ненулевой фрейм, если вы переместите код установки ограничения за пределы закрытия конфигурации, то он всегда работает независимо от того, есть фрейм с областью или нет!

override func loadView() {

    view = UIView()
    view.backgroundColor = .yellow

    let someView = SomeView(frame:CGRect(x: 0, y: 0, width: 20, height: 20))~>{

        $0.backgroundColor = .blue
        $0.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview($0)
    }

    NSLayoutConstraint.activate([
        someView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: margin),
        someView.topAnchor.constraint(equalTo: view.topAnchor, constant: margin),
        someView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -margin),
        someView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -margin)
    ])
}

Что нас смущает, так это предыдущий код, $0 внутри замыкания, является строгой ссылкой на вновь созданное представление (и родительский элемент также содержит ссылку на него, когда он был добавлен как подпредставление, поэтому он не выгружается), и эта точно такая же ссылка назначена на someView снаружи, где он затем выполняет точно такой же код, который был в замыкании. Между закрытием и кодом снаружи ничего нет, кроме этого присваивания, но оно дает разные результаты! Но почему?

Может кто-нибудь объяснить, почему мы видим такое поведение? Чего нам не хватает?

1 Ответ

0 голосов
/ 15 марта 2020

Я бы сказал, что игровые площадки - это работа дьявола. Я запустил ваш код в реальном iOS проекте приложения, и он работал, как и ожидалось, даже с

SomeView(frame:CGRect(x: 0, y: 0, width: 20, height: 20))

В принципе, я бы не ожидал, что игровая площадка будет go на протяжении всей жизни этапы контроллера представления правильно.

...