UIStackView Анимация Проблема - PullRequest
0 голосов
/ 14 октября 2018

У меня есть subStackView внутри stackView, и когда я скрываю / показываю содержимое ОДНОГО subStackView, анимация идет вверх по всем остальным представлениям стека: https://www.youtube.com/watch?v=vKXwX7OpkxU

Вот как я создаюsubStackView.Я попытался с и без clipToBounds и с без переведенного AutosizingMaskIntoConstraints.Также пробовал layoutIfNeeded в анимации.

let subStackView = UIStackView(arrangedSubviews: [self.innerView[0], self.innerView[1])
subStackView.translatesAutoresizingMaskIntoConstraints = false
subStackView.axis = .vertical
subStackView.distribution = .fillEqually
subStackView.alignment = .fill
subStackView.spacing = 0
subStackView.clipsToBounds = true

Этот subStackView затем загружается в mainStackView, что приводит к проблеме.

1 Ответ

0 голосов
/ 14 октября 2018

Одним из способов решения вашей проблемы является более непосредственное управление отображением и скрытием фиолетового изображения.То, что вы делаете сейчас (я полагаю), устанавливает для свойства isHidden значение true, а затем позволяет представлению стека делать все, что ему нужно.Вместо этого давайте поместим фиолетовое представление в представление контейнера и анимируем высоту представления контейнера до нуля.Тогда это может выглядеть следующим образом:

demo

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

Итак, вот что я сделал для демонстрации.Я сделал ярлык «Здравствуй, мир!» На фиолетовом фоне.Я ограничил его высоту до 80. Я поместил метку в контейнерный вид (просто UIView).Я ограничил верхнюю, переднюю и заднюю кромки метки видом контейнера, как обычно.Я также ограничил нижний край метки видом контейнера, но с приоритетом 999 * (что меньше, чем заданный по умолчанию, «обязательный» приоритет 1000).Это означает, что представление контейнера будет очень стараться иметь тот же размер, что и метка, но если представление контейнера будет вынуждено изменять высоту, это будет происходить без влияния на высоту метки.

Контейнер также имеетclipsToBounds, поэтому, если контейнер становится короче метки, нижняя часть метки скрыта.

Чтобы включить видимость метки, я активирую или деактивирую ограничение высоты требуемого приоритета навид контейнера, который устанавливает его высоту на ноль.Затем я прошу окно выложить свои дочерние элементы внутри блока анимации.

В моей демонстрации у меня также стэк-вид spacing установлен на 12. Если я просто оставлю вид контейнера «видимым» (не isHidden) с высотой ноль, вид стека поместит 12 точек пространства после кнопки, что может выглядеть неправильно.В iOS 11 и более поздних версиях я исправляю это, устанавливая настраиваемый интервал 0 после кнопки, когда я «скрываю» контейнер, и восстанавливаю интервал по умолчанию, когда я «показываю» его.

В версии iOS до iOS11, я просто продолжаю и действительно скрываю контейнер (устанавливая его isHidden в true) после завершения скрывающей анимации.И я показываю контейнер (устанавливая его isHidden в false) перед запуском показа анимации.Это приводит к небольшому толчку, так как интервал мгновенно исчезает или появляется снова, но это не так уж и плохо.

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

В любом случае, вот мой код:

class TaskletViewController: UIViewController {

    @IBAction func buttonWasTapped() {
        if detailContainerHideConstraint == nil {
            detailContainerHideConstraint = detailContainer.heightAnchor.constraint(equalToConstant: 0)
        }
        let wantHidden = !(detailContainerHideConstraint?.isActive ?? false)

        if wantHidden {
            UIView.animate(withDuration: 0.25, animations: {
                if #available(iOS 11.0, *) {
                    self.stackView.setCustomSpacing(0, after: self.button)
                }
                self.detailContainerHideConstraint?.isActive = true
                self.view.window?.layoutIfNeeded()
            }, completion: { _ in
                if #available(iOS 11.0, *) { } else {
                    self.detailContainer.isHidden = true
                }
            })
        } else {
            if #available(iOS 11.0, *) { } else {
                detailContainer.isHidden = false
            }
            UIView.animate(withDuration: 0.25, animations: {
                if #available(iOS 11.0, *) {
                    self.stackView.setCustomSpacing(self.stackView.spacing, after: self.button)
                }
                self.detailContainerHideConstraint?.isActive = false
                self.view.window?.layoutIfNeeded()
            })
        }
    }

    override func loadView() {
        stackView.axis = .vertical
        stackView.spacing = 12
        stackView.translatesAutoresizingMaskIntoConstraints = false

        button.translatesAutoresizingMaskIntoConstraints = false
        button.backgroundColor = UIColor.green.withAlphaComponent(0.2)
        button.setTitle("Tap to toggle", for: .normal)
        button.addTarget(self, action: #selector(buttonWasTapped), for: .touchUpInside)
        button.setContentHuggingPriority(.required, for: .vertical)
        button.setContentCompressionResistancePriority(.required, for: .vertical)
        stackView.addArrangedSubview(button)

        detailLabel.translatesAutoresizingMaskIntoConstraints = false
        detailLabel.text = "Hello, world!"
        detailLabel.textAlignment = .center
        detailLabel.backgroundColor = UIColor.purple.withAlphaComponent(0.2)
        detailLabel.heightAnchor.constraint(equalToConstant: 80).isActive = true

        detailContainer.translatesAutoresizingMaskIntoConstraints = false
        detailContainer.clipsToBounds = true
        detailContainer.addSubview(detailLabel)
        let bottomConstraint = detailLabel.bottomAnchor.constraint(equalTo: detailContainer.bottomAnchor)
        bottomConstraint.priority = .init(999)
        NSLayoutConstraint.activate([
            detailLabel.topAnchor.constraint(equalTo: detailContainer.topAnchor),
            detailLabel.leadingAnchor.constraint(equalTo: detailContainer.leadingAnchor),
            detailLabel.trailingAnchor.constraint(equalTo: detailContainer.trailingAnchor),
            bottomConstraint
        ])
        stackView.addArrangedSubview(detailContainer)

        self.view = stackView
    }

    private let stackView = UIStackView()
    private let button = UIButton(type: .roundedRect)
    private let detailLabel = UILabel()
    private let detailContainer = UIView()
    private var detailContainerHideConstraint: NSLayoutConstraint?
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...