Одним из способов решения вашей проблемы является более непосредственное управление отображением и скрытием фиолетового изображения.То, что вы делаете сейчас (я полагаю), устанавливает для свойства isHidden
значение true, а затем позволяет представлению стека делать все, что ему нужно.Вместо этого давайте поместим фиолетовое представление в представление контейнера и анимируем высоту представления контейнера до нуля.Тогда это может выглядеть следующим образом:
![demo](https://i.stack.imgur.com/h2Lc6.gif)
Причина использования контейнера вместо простой анимации высоты фиолетового представления заключается в том, что вы можете (вВообще) есть другие ограничения, управляющие высотой фиолетового вида, поэтому ограничение его высоты до нуля приведет к заполнению вашей консоли неудовлетворительными ошибками ограничения.
Итак, вот что я сделал для демонстрации.Я сделал ярлык «Здравствуй, мир!» На фиолетовом фоне.Я ограничил его высоту до 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?
}