Скрытое свойство не может быть изменено в блоке анимации
У меня есть два 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 раза). Я нашел это, когда вломился в закрытие анимации и увидел, что свойство не изменит его назначение. Теперь я замечаю ту же проблему, которая кажется случайно случайной. Значением по умолчанию для второй метки является 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
}
}
Ответы
Ответ 1
В iOS 11 и ранее при скрытии arrangedSubview
of UIStackView
с использованием API анимации UIView несколько раз значения скрытых свойств "стекают", и для этого требуется установить скрытый код 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, проверьте образец проекта, который четко воспроизводит его.
![введите описание изображения здесь]()
Ответ 2
Кажется, что существует корреляция того, сколько раз скрытый флаг установлен в одно и то же состояние, и сколько раз он должен устанавливать в другое состояние до того, как он действительно изменится. В моем случае у меня был скрытый флаг, уже установленный в YES, прежде чем он снова будет установлен в YES снова в блоке анимации, и это вызвало проблему, когда мне пришлось дважды вызвать скрытый = НЕТ в моем другом блоке анимации, чтобы снова увидеть его. Если бы я добавил несколько скрытых строк = YES в первый блок анимации для одного и того же представления, мне пришлось бы иметь более скрытые строки = НЕТ во втором блоке анимации. Это может быть ошибкой в UIStackView KVO наблюдения для скрытого флага, который не проверяет, действительно ли значение было изменено или нет, прежде чем изменять какое-то внутреннее состояние, которое приводит к этой проблеме.
Чтобы временно исправить проблему (пока Apple не установит ее), я сделал категорию для UIView и swizzled 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
Ответ 3
При рассмотрении ошибки UIStackView я решил проверить скрытое свойство.
if myView.hidden != hidden {
myView.hidden = hidden
}
Это не самое элегантное решение, но оно работает для меня.
Ответ 4
Появляется ошибка Apple с UIStackView. См. Следующее...
UIStackView: включение скрытых анимаций застревает в скрытом mode http://www.openradar.me/22819594
Мое решение, хотя и не идеальное, заключалось в том, чтобы скрыть UIStackView без анимации.
Ответ 5
В соответствии с ответом 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()
Ответ 6
Что сработало для меня - установить скрытое свойство вне анимации, а затем анимировать layoutIfNeeded(), как и с анимационными ограничениями:
label.isHidden = true
UIView.animate(withDuration: 3) {
self.view.layoutIfNeeded()
}
где метка представляет собой упорядоченное подчинение UIStackView.