隐藏属性不能在animation块中更改

我在UIStackView中embedded了两个UILabel。 顶部标签始终保持可见,但底部标签通过hidden属性来打开和closures。 我想这个效果是animation的,所以我把它卡在一个animation块中:

 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次)。 我发现这一点,而闯入animation封闭,看到该属性不会改变它的任务。 现在我注意到同样的问题似乎随机再次发生。 第二个标签的默认值是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 } } 

在iOS 11和之前版本中,当UIStackView使用UIViewanimationAPI隐藏UIStackViewarrangedSubviewUIStackView ,隐藏的属性值“stack”,并且在值实际更改之前需要多次隐藏设置为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,请查看清楚重现的示例项目 。

在这里输入图像说明

隐藏标志被设置为相同状态的次数似乎是相关的,并且在它实际改回之前它必须设置为不同状态的次数。 在我的情况下,隐藏标志已经设置为YES,然后在animation块中再次设置为YES,导致我必须在其他animation块中调用hidden = NO两次以使其再次可见的问题。 如果我在同一视图的第一个animation块中添加了更多的hidden = YES行,那么我必须在第二个animation块中包含更多的hidden = NO行。 这可能是UIStackView的KVO观察隐藏标志的一个错误,它在检查导致此问题的某些内部状态之前是否不检查该值是否实际发生了更改。

为了暂时解决这个问题(直到苹果修复了这个问题),我为UIView做了一个分类,并将setHidden:方法debugging为一个版本,该版本首先检查原始值,并且只在与原始值不同时设置新值。 这似乎没有任何不良影响。

 @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 

在考虑UIStackView错误时,我决定检查隐藏的属性。

 if myView.hidden != hidden { myView.hidden = hidden } 

这不是最优雅的解决scheme,但它适用于我。

看起来是UIStackView的一个苹果bug。 看下面…

UIStackView:用animation隐藏切换卡在隐藏模式http://www.openradar.me/22819594

我的解决scheme,虽然不理想,但是没有animation就隐藏了UIStackView。

根据Raz0的回答,谁发现只有在必要的情况下isHidden设置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() 

对我而言,效果是在animation之外设置隐藏属性,然后使用layoutIfNeeded()animation,就像animation约束一样:

 label.isHidden = true UIView.animate(withDuration: 3) { self.view.layoutIfNeeded() } 

其中标签是UIStackView的排列子视图。