隐藏属性无法在动画块中更改

时间:2015-10-20 15:26:05

标签: ios swift uiviewanimation

我在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
    }
 }

6 个答案:

答案 0 :(得分:27)

在iOS 11及更早版本中,当多次使用UIView动画API隐藏arrangedSubview UIStackView时,隐藏属性值为“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中的一个错误,请查看可以清楚再现它的sample project

enter image description here

答案 1 :(得分:7)

隐藏标志设置为相同状态的次数以及在实际更改之前必须设置为不同状态的次数似乎存在关联。在我的情况下,我已经将隐藏标志设置为YES,然后在动画块中再次设置为YES,这导致了我必须在我的其他动画块中调用hidden = NO两次以使其再次可见的问题。如果我在同一个视图的第一个动画块中添加了更多的hidden = YES行,那么我在第二个动画块中也必须有更多的hidden = NO行。这可能是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

答案 2 :(得分:6)

似乎是UIStackView的Apple漏洞。请参阅以下内容......

  

UIStackView:用动画隐藏的切换卡在隐藏中   模式http://www.openradar.me/22819594

我的解决方案虽然不是很理想,但却是在没有动画的情况下隐藏UIStackView。

答案 3 :(得分:5)

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

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

这不是最优雅的解决方案,但它对我有用。

答案 4 :(得分:1)

根据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()

答案 5 :(得分:0)

对我来说有用的是在动画之外设置隐藏属性,然后设置layoutIfNeeded()动画,就像使用动画约束一样:

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

其中label是UIStackView的排列子视图。