堆栈视图动画 - 排列子视图的折叠

10

我必须使用stackview作为父视图。我正在尝试将具有两行的stackview动画化,以获得折叠和充气底部行的效果。 你可以说,我正在尝试做的是将此代码应用于具有子视图的常规自动布局视图时所获得的相同效果:

func showView()
{
    if(!expand)
    {  UIView.animateWithDuration(0.5, animations:{
        self.expandableViewHeight.constant = 50
        // Update layout of all subviews
        self.parentViewController!.view.layoutIfNeeded()})
    } else {   
      UIView.animateWithDuration(0.5, animations:{
        self.expandableViewHeight.constant = 100
        // Update layout of all subviews
        self.parentViewController!.view.layoutIfNeeded()})
    }
}

简洁明了。当您点击此视图时,它将展开或折叠,并与所有子视图一起进行动画效果。随着动画的进行,子视图被裁剪(而不是调整大小/缩放),最终您只能看到原始视图的一半。

看起来很简单,但我无法使用stackview完成它。 当我向stackview添加第二行,然后像在几个教程中那样对layoutIfNeeded()执行动画时: - 当充气底部行从左侧移动到其位置, - 当折叠底部行时,它只是消失了(没有动画) - 只有背景视图可以正确地进行动画处理(请参见代码)

当我使用高度约束并且不在底部行上进行layoutIfNeeded()动画时,则: - 当充气底部行时,它会随着动画逐渐缩放到完整高度 - 当折叠时,底部行会随着动画逐渐缩放为0

无法使其裁剪底部行!任何提示都将不胜感激! :)

这是我的stackview代码:

override func viewDidLoad(){
    super.viewDidLoad()

    background = UIView(frame:CGRectMake(0, 0, frame.width,frame.height))
    background.backgroundColor = UIColor.whiteColor()
    background.layer.cornerRadius = 5
    background.layer.masksToBounds = true

    self.addSubview(background)

    vStack = UIStackView()
    vStack.axis = .Vertical
    vStack.alignment = .Fill
    vStack.distribution = .Fill
    vStack.spacing = 0

    self.addArrangedSubview(vStack)

    hStack = UIStackView()
    hStack.axis = .Horizontal
    hStack.alignment = .Center
    hStack.distribution = .EqualSpacing
    hStack.spacing = 10

    hStack2 = UIStackView()
    hStack2.axis = .Horizontal
    hStack2.alignment = UIStackViewAlignment.Center
    hStack2.distribution = UIStackViewDistribution.EqualCentering
    hStack2.spacing = 10

    lquestionStack = UIStackView()
    questionStack.axis = .Horizontal
    questionStack.alignment = .Center
    questionStack.distribution = .EqualSpacing
    questionStack.spacing = QUESTION_PADDING


    let labelQuestionNumber = UIButton()
    labelQuestionNumber.userInteractionEnabled = false
    let numberImage = UIImage(named: "backgroundImage")
    labelQuestionNumber.setBackgroundImage(numberImage, forState: .Normal)

    questionStack.addArrangedSubview(labelQuestionNumber)


    hStack.addArrangedSubview(questionStack)
    vStack.addArrangedSubview(hStack)

    hStack2.addArrangedSubview(questionStack)
    vStack.addArrangedSubview(hStack2)

 }
 public func collapseInflateAction() {
    if checkWidget.collapsed {
        inflateWidget()
    } else {
        collapseWidget()
    }
    checkWidget.collapsed = !checkWidget.collapsed
}

private func collapseWidget(){
    vStack.removeArrangedSubview(self.hStack2)
    self.hStack2.removeFromSuperview()

    UIView.animateWithDuration(0.5, animations: { () -> Void in
        self.background.frame.size.height = (self.background.frame.size.height)/2
        self.layoutIfNeeded()
        self.superview?.layoutIfNeeded()
        })
}

private func inflateWidget(){
    self.vStack.addArrangedSubview(self.hStack2)

    UIView.animateWithDuration(0.5, animations: { () -> Void in
        self.background.frame.size.height = self.background.frame.size.height*2
        self.layoutIfNeeded()
        self.superview?.layoutIfNeeded()
    })

}

这是最后两种方法的变体,它使用约束而不是layoutIfNeeded()动画。并且被修改的约束也不同:

override func viewDidLoad(){
    super.viewDidLoad()
(...)
 heightConstraint = NSLayoutConstraint(item: hStack2, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: 0)
    self.addConstraint(heightConstraint)
}
private func collapseWidget(){
    UIView.animateWithDuration(0.5, animations: { () -> Void in
        self.background.frame.size.height = 44
        self.heightConstraint.constant = 0
        self.superview?.layoutIfNeeded()
        })
}

private func inflateWidget(){
    UIView.animateWithDuration(0.5, animations: { () -> Void in
        self.background.frame.size.height = 88
        self.heightConstraint.constant = 44
        self.superview?.layoutIfNeeded()
    })

}

2
想知道您是否解决了问题,考虑到您的问题已经很久了。能否分享一下解决方法? - ldiqual
2个回答

3
在这种情况下,可以帮助人们顺畅地运行他们的动画的方法是在层次结构中的最高视图上触发layoutIfNeeded()
当您的UIStackView嵌入到UIScrollView中时,这特别有帮助。如果您只在arrangedSubviewsUIStackView上触发layoutIfNeeded,则会出现奇怪的故障。
另一个解决方案是简单地在您的UIViewController视图上触发layoutIfNeeded
希望这个答案能够帮助到您。

3

那是很久以前的事了,但我认为在布局父视图之前添加self.layoutIfNeeded()有助于解决问题。

   fileprivate func collapseWidget(){

    UIView.animate(withDuration: 0.25, animations: { () -> Void in
        self.background.frame.size.height = self.heightCollapsed
        self.heightConstraint.constant = self.heightCollapsed
        self.layoutIfNeeded()
        self.superview?.layoutIfNeeded()
        })
}

fileprivate func inflateWidget(){
    self.separatorView.isHidden = false
    UIView.animate(withDuration: 0.25, animations: { () -> Void in
        self.background.frame.size.height = self.heightInflated
        self.heightConstraint.constant = self.heightInflated
        self.layoutIfNeeded()
        self.superview?.layoutIfNeeded()
    })
}

我不知道为什么需要这个,但它有效。我只需要将self.layoutIfNeeded()更改为self.superview?.layoutIfNeeded()(不需要两者都有)。 - malvarez

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接