用Swift连续动画UIViews数组

时间:2018-01-17 10:04:11

标签: ios animation recursion uiview map-function

我有一组视图(UIStackView.arrangedSubviews),我想用UIView.animate()制作动画。但是我只想在前一个元素完成时开始动画下一个元素。

我想找到一个有点“优雅”的解决方案。我尝试了两种不同的方法,但我对它们都很困惑。我怎样才能做到这一点?

第一次尝试:递归关闭

let animate = { (views: [UIView]) -> () in
    guard let view = views.first else { return }
    UIView.animate(
        withDuration: 0.5,
        animations: {
            view.frame.origin.y -= 30.0
            view.alpha = 1.0
    },
        completion: { finished in
            if finished {
                // TODO: Only start animating next view when previous is finished?
                animate(views.removeFirst()) // ERROR: Variable used within itself
            }
    })
}

第二次尝试:使用数组映射

let _ = stackView.arrangedSubviews.map { view -> UIView in
    view.frame.origin.y += 30.0
    UIView.animate(
        withDuration: 0.5,
        animations: {
            view.frame.origin.y -= 30.0
            view.alpha = 1.0
        },
        completion: { finished in
            // TODO: Only start animating next view when previous is finished?
    })

    return view
}

1 个答案:

答案 0 :(得分:3)

如何枚举它们,并使用completion制作动画,但使用delay参数?您可以将i视图的延迟计算为i * animDuration

let animDuration = 0.5
for (index, view) in stackView.arrangedSubviews.enumerated() {
    view.frame.origin.y += 30.0
    UIView.animate(withDuration: animDuration, delay: animDuration * Double(index), options: [.curveLinear], animations: {
        view.alpha = 1
        view.frame.origin.y -= 30.0
    }, completion: nil)
}

P.S。:正如旁注,UIStackView使用自动布局来布置其排列的子视图。小心直接设置框架。

修改

虽然我认为您可以非常确定使用delay可以按预期工作,但您可以尝试使用UIViewPropertyAnimator个对象来链接动画:

var animators: [UIViewPropertyAnimator] = []
stackView.arrangedSubviews.forEach({ (view) in
    let animator = UIViewPropertyAnimator(duration: 0.5, timingParameters: UICubicTimingParameters(animationCurve: .linear))
    view.frame.origin.y += 30.0
    animator.addAnimations {
        view.alpha = 1
        view.frame.origin.y -= 30.0
    }
    // start this animator in completion of the previous one (if there is previous one)
    let previousAnimator = animators.last
    previousAnimator?.addCompletion({ (_) in
        animator.startAnimation()
    })

    // append new animator to animators
    animators.append(animator)
})

// now you should be able to run the chain by starting the first animator
animators.first?.startAnimation()