Скрытое свойство не может быть изменено в блоке анимации
У меня есть две UILabels, встроенные в UIStackView. Верхняя метка остается видимой постоянно, но нижняя метка включается и выключается с помощью свойства hidden
. Я хотел, чтобы этот эффект был анимирован, поэтому я вставил его в анимационный блок:
private func toggleResultLabel(value:Double) {
if value == 0 {
UIView.animateWithDuration(0.25) { () -> Void in
self.resultLabel.hidden = true
}
} else {
UIView.animateWithDuration(0.25) { () -> Void in
// Something weird is happening. I had to add 3 of the same statements to get
// the hidden flag to be false
self.resultLabel.hidden = false
self.resultLabel.hidden = false
self.resultLabel.hidden = false
}
}
}
Проблема в том, что скрытое свойство не изменится, если я не буду повторять это утверждение снова и снова (в данном случае 3 раза). Я нашел это, когда взломал закрытие анимации и увидел, что свойство не изменится на it's назначение. Теперь я замечаю, что та же проблема возникает, казалось бы, случайно снова. Значение по умолчанию для второй метки равно true
, Если это уместно.
Есть ли что-то, что я здесь упускаю, или это ошибка?
Обновление :
Как бы то ни было, я заставил его работать, добавив removeArrangedSubview()
и addArrangedSubview()
:
if value == 0 {
UIView.animateWithDuration(0.25) { () -> Void in
self.resultLabel.hidden = true
self.heroStackView.removeArrangedSubview(self.resultLabel)
}
} else {
UIView.animateWithDuration(0.25) { () -> Void in
self.heroStackView.addArrangedSubview(self.resultLabel)
self.resultLabel.hidden = false
}
}
6 ответов:
В iOS 11 и ранее, при скрытии
arrangedSubview
изUIStackView
с помощью UIView animation API несколько раз, скрытые значения свойств "стекаются", и это требует установки скрытого значенияfalse
несколько раз, прежде чем значение действительно изменится.В работе мы решили использовать расширение UIView с обходным методом, который устанавливает скрытый только один раз для данного значения.
extension UIView { // Workaround for the UIStackView bug where setting hidden to true with animation // mulptiple times requires setting hidden to false multiple times to show the view. public func workaround_nonRepeatingSetHidden(hidden: Bool) { if self.hidden != hidden { self.hidden = hidden } } }
Это определенно ошибка в UIKit, проверьте образец проекта , который воспроизводит его ясно.
Похоже, существует корреляция между тем, сколько раз скрытый флаг устанавливается в одно и то же состояние и сколько раз он должен быть установлен в другое состояние, прежде чем он действительно изменится обратно. В моем случае, у меня был скрытый флаг, уже установленный в YES, прежде чем он снова был установлен в YES в блоке анимации, и это вызвало проблему, когда я должен был вызвать hidden = NO дважды в моем другом блоке анимации, чтобы получить его видимым снова. Если я добавил больше скрытых = YES строк в первом блоке анимации для того же вида, я должен был иметь больше скрытых = Во втором блоке анимации тоже нет строк. Это может быть ошибка в наблюдении KVO UIStackView для скрытого флага, который не проверяет, действительно ли значение изменено или нет, прежде чем изменить некоторое внутреннее состояние, которое приводит к этой проблеме.
Чтобы временно устранить проблему (до тех пор, пока Apple не исправит ее), я создал категорию для UIView и перевел метод setHidden: в версию, которая сначала проверяет исходное значение и устанавливает новое значение, только если оно отличается от исходного. Это, кажется, работает. без каких-либо вредных последствий.
@implementation UIView (MethodSwizzling) + (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ Class class = [self class]; SEL originalSelector = @selector(setHidden:); SEL swizzledSelector = @selector(UIStackViewFix_setHidden:); Method originalMethod = class_getInstanceMethod(class, originalSelector); Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)); if (didAddMethod) { class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); } else { method_exchangeImplementations(originalMethod, swizzledMethod); } }); } - (void)UIStackViewFix_setHidden:(BOOL)hidden { if (hidden != self.hidden) { [self UIStackViewFix_setHidden:hidden]; } } @end
Похоже на ошибку Apple с UIStackView. Увидеть следующее...
UIStackView: toggleing hidden с анимацией застревает в hidden режим http://www.openradar.me/22819594
Моим решением, хотя и не идеальным, было скрыть UIStackView без анимации.
Рассматривая ошибку UIStackView, я решаю проверить скрытое свойство.
Это не самое элегантное решение, но оно работает для меня.if myView.hidden != hidden { myView.hidden = hidden }
Согласно ответу
Raz0
, который обнаружил, что только установкаisHidden
при необходимости решает проблему, вот быстрый обходной путь, который заставил его работать для меня. Я избегаю метода swizzling, потому что он изначально небезопасен, в пользу подходаshow/hide
, который не должен путаться с оригинальными методами:extension UIView { func show() { guard isHidden else { return } isHidden = false } func hide() { guard !isHidden else { return } isHidden = true } }
Используйте его так:
view.show() view.hide()