动手打造Swift声明式动画框架-1

在iOS中做动画,还比较简单。因为苹果已经提供好了api,设置相应的参数就好。如下,一句简单的代码,就可做frame的动画。

UIView.animate(withDuration: 0.3) {
    button.frame.size = CGSize(width: 200, height: 200)
}

但是,如果我们想要在动画结束之后,再做其他的动画,就需要在completion block中嵌套。写法上不太直观,也不利于阅读。当这种需求越多,block的嵌套也就越深。代码会看的很费劲。

UIView.animate(withDuration: 0.3, animations: {
    button.alpha = 1
}, completion: { _ in
    UIView.animate(withDuration: 0.3) {
        button.frame.size = CGSize(width: 200, height: 200)
    }
})

于是,就出现了声明式的api。

声明式:简单,易读,便于理解。把我们要做的事情写好,一目了然。

声明式动画

下面的代码,看起来就清晰多了。

button.animate([
    .fadeIn(duration: 0.3),
    .resize(to: CGSize(width: 200, height: 200), duration: 0.3)
])

fadeIn:做alpha的动画。
resize:做缩放的动画。

实现

准备

新建playground。因为我们要看到实时的view,所以需要import PlaygroundSupport。新建view后,再添加一句。打开Assistant Editor(带2个圆圈的按钮),在右边就可以看得到view。

PlaygroundPage.current.liveView = view
数据结构

既然要做声明式的api,那就需要将UIView.animate这个方法封装起来,在内部调用。所以需要一个结构来存储动画时间,如何做动画(block)。

public struct Animation {
    public let duration: TimeInterval
    public let closure: (UIView) -> Void
}

fadeIn,resize动画,只是封装一层而已。

public extension Animation {
    static func fadeIn(duration: TimeInterval = 0.3) -> Animation {
        return Animation(duration: duration, closure: { $0.alpha = 1 })
    }

    static func resize(to size: CGSize, duration: TimeInterval = 0.3) -> Animation {
        return Animation(duration: duration, closure: { $0.bounds.size = size })
    }
}
动画

扩展UIView,添加animate方法,这样每个view就可以直接调用。参数是Animation的数组,目的是为了可以执行一系列的动画。在UIView.animate的completion block中递归调用animate,形成one by one的动画。

public extension UIView {
    func animate(_ animations: [Animation]) {
        // Exit condition: once all animations have been performed, we can return
        guard !animations.isEmpty else {
            return
        }

        // Remove the first animation from the queue
        var animations = animations
        let animation = animations.removeFirst()

        // Perform the animation by calling its closure
        UIView.animate(withDuration: animation.duration, animations: {
            animation.closure(self)
        }, completion: { _ in
            // Recursively call the method, to perform each animation in sequence
            self.animate(animations)
        })
    }
}
Demo

fadeIn先执行完,resize再执行。

let animationView = UIView(frame: CGRect(
    x: 0, y: 0,
    width: 50, height: 50
))

animationView.backgroundColor = .red
animationView.alpha = 0
view.addSubview(animationView)

animationView.animate([
    .fadeIn(duration: 3),
    .resize(to: CGSize(width: 200, height: 200), duration: 3)
])

并行动画

上面的实现是队列式的动画,一个动画执行完之后,才会执行另外一个。若要实现并行的动画呢?也简单,就不用等到动画结束后才去执行下一个,直接便利执行即可。

public extension UIView {
    func animate(inParallel animations: [Animation]) {
        for animation in animations {
            UIView.animate(withDuration: animation.duration) {
                animation.closure(self)
            }
        }
    }
}

调用:

animationView.animate(inParallel: [
    .fadeIn(duration: 3),
    .resize(to: CGSize(width: 200, height: 200), duration: 3)
])

参考:
Building a declarative animation framework in Swift - Part 1

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 14,216评论 4 61
  • 如果想让事情变得顺利,只有靠自己--夏尔·纪尧姆 上一章介绍了隐式动画的概念。隐式动画是在iOS平台创建动态用户界...
    夜空下最亮的亮点阅读 6,057评论 0 1
  • 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥iOS动画全貌。在这里你可以看...
    F麦子阅读 10,526评论 5 13
  • 蛋糕上面铺着鲜红色的草莓和雪白雪白的奶油,它们看起来像一朵朵花儿一样很美丽!现在蛋糕放在冰箱里面,等把蛋糕拿出来吃...
    祉延阅读 3,247评论 3 6
  • 彤的头发这些天白了不少。 总算新厂调试完毕,点火成功。两年多了虽然还有一大堆需要签字确认的手续还得加紧时间去办,但...
    蜀山袖手人阅读 2,118评论 3 9