stackview 动画 - 排列的子视图的折叠

Posted

技术标签:

【中文标题】stackview 动画 - 排列的子视图的折叠【英文标题】:stackview animation - collapse of arranged subviews 【发布时间】:2015-11-13 19:36:22 【问题描述】:

我必须使用 stackview 作为父视图。我正在尝试用 2 行动画 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

不能让它夹在最下面一行!任何提示表示赞赏! :)

这是我的堆栈视图代码:

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


【问题讨论】:

想知道您是否找到了解决问题的方法,因为您的问题已经存在多久了。介意分享一下吗? 【参考方案1】:

那是很久以前的事了,但我认为在布局超级视图之前添加 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()(两者都不需要)【参考方案2】:

在这种情况下,可以帮助人们使动画顺利运行的方法是在层次结构中的最高视图上触发layoutIfNeeded()

当您的 UIStackView 嵌入到 UIScrollView 中时,这特别有用。如果您只在arrangedSubviewsUIStackView 上触发layoutIfNeeded,您最终会在动画中出现奇怪的故障。

另一种解决方案过于简单地在您的 UIViewController 视图上触发 layoutIfNeeded

希望这个答案会有所帮助。

【讨论】:

以上是关于stackview 动画 - 排列的子视图的折叠的主要内容,如果未能解决你的问题,请参考以下文章

即使隐藏了排列的子视图,UIStackView 似乎也会堆叠间距

如何向 StackView 的子视图内的视图添加约束

如何在 StackView 内的两个视图之间进行动画处理?

堆栈视图,将空间折叠到内容

如何正确地为隐藏在 UITableViewCell 中的 UIStackView 设置动画?

UIStackView:基于所有子视图的一致高度,即使它们丢失