隐藏属性不能在动画块内更改
Posted
技术标签:
【中文标题】隐藏属性不能在动画块内更改【英文标题】:Hidden property cannot be changed within an animation block 【发布时间】:2015-10-20 15:26:05 【问题描述】:我在 UIStackView 中嵌入了两个 UILabel。顶部标签始终可见,但底部标签通过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
【问题讨论】:
不改变的值很奇怪,但无论如何要为您的标签设置动画,您应该更改视图的alpha
值而不是hidden
。 AFAIK,hidden
不可动画。
感谢@GuillaumeAlgis!我可以尝试更改 alpha,但我认为它不会重新排列 StackView,因为它只是不可见并被删除。 hidden 属性是可动画的,并且在 95% 的时间里都有效。作为参考,我使用了页面底部的部分:https://developer.apple.com/library/prerelease/tvos/documentation/UIKit/Reference/UIStackView_Class_Reference/index.html
嗯,如果我正确理解了文档,这是UIStackView
的特定行为。在这种特殊情况之外 hidden
不会被动画化。您使用的是UIStackView
吗?
@GuillaumeAlgis 是的,我是。
哦,是的,刚刚在问题中看到了,对不起,读得太快了:|
【参考方案1】:
在 ios 11 及之前的版本中,当使用 UIView 动画 API 多次隐藏 UIStackView
的 arrangedSubview
时,隐藏的属性值为“stack”,它需要在实际值之前将 hidden 设置为 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 的一个 bug,查看sample project 清楚地重现了它。
【讨论】:
2017 年 7 月,bug 还在,刚刚重现。感谢您详尽的回答。 iOS 11 也出现了这种情况。 很好的答案!很有帮助! iOS 13 也有这种情况。 在 iOS 14.2 中仍然是一个问题。建议的解决方案奏效了。【参考方案2】:似乎隐藏标志设置为相同状态的次数以及在实际改回之前必须设置为不同状态的次数之间存在相关性。就我而言,在动画块中再次将其设置为 YES 之前,我已经将隐藏标志设置为 YES,这导致了我必须在另一个动画块中调用 hidden = NO 两次才能使其再次可见的问题。如果我在同一个视图的第一个动画块中添加了更多 hidden = YES 行,那么我也必须在第二个动画块中添加更多 hidden = NO 行。这可能是 UIStackView 对隐藏标志的 KVO 观察中的一个错误,它不会在更改导致此问题的某些内部状态之前检查值是否实际更改。
为了临时解决这个问题(直到 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
【讨论】:
【参考方案3】:在考虑 UIStackView 错误时,我决定检查隐藏属性。
if myView.hidden != hidden
myView.hidden = hidden
这不是最优雅的解决方案,但对我有用。
【讨论】:
【参考方案4】:似乎是 UIStackView 的 Apple 错误。请参阅以下内容...
UIStackView:用动画切换隐藏会卡在隐藏状态 模式http://www.openradar.me/22819594
我的解决方案虽然不理想,但还是隐藏 UIStackView 而没有动画。
【讨论】:
【参考方案5】:根据Raz0
的回答,谁发现只有在必要时设置isHidden
才能解决问题,这里有一个快速的解决方法,使它对我有用。我正在避免方法混用,因为它本质上是不安全的,支持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】:对我有用的是在动画之外设置 hidden 属性,然后为 layoutIfNeeded() 设置动画,就像使用动画约束一样:
label.isHidden = true
UIView.animate(withDuration: 3)
self.view.layoutIfNeeded()
其中 label 是 UIStackView 的排列子视图。
【讨论】:
以上是关于隐藏属性不能在动画块内更改的主要内容,如果未能解决你的问题,请参考以下文章
UIStackView中UILabels上的动画隐藏属性导致不同的动画