隐藏属性不能在动画块内更改

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 多次隐藏 UIStackViewarrangedSubview 时,隐藏的属性值为“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 的排列子视图。

【讨论】:

以上是关于隐藏属性不能在动画块内更改的主要内容,如果未能解决你的问题,请参考以下文章

css转换后隐藏元素

UIStackView中UILabels上的动画隐藏属性导致不同的动画

swiper 更改indicator-dots 属性 隐藏面板指示点

如何在动画结束时设置属性或任何回调? [复制]

取消隐藏 UIStackView 元素时的动画方向

copyfile 的参数是不是会更改隐藏属性?