优雅地书写 UIView 动画

浏览: 121 发布日期: 2016-12-14 分类: swift

原文: Swift: UIView Animation Syntax Sugar
作者: Andyy Hope
译者: kemchenj

闭包成对出现时会恶心到你

Swift 代码里的闭包是很好用的工具, 它们是一等公民, 如果他们在 API 的尾部时还可以变成尾随闭包, 并且现在 Swift 3 里还默认noescape 以避免循环引用.

但每当我们不得不使用那些包含了多个闭包参数的API的时候, 就会让这门优雅的语言变得很丑陋. 是的, 我说的就是你, UIView.

class func animate(withDuration duration: TimeInterval,            
    animations: @escaping () -> Void,          
    completion: ((Bool) -> Void)? = nil)

<!--more-->

尾随闭包

UIView.animate(withDuration: 0.3, animations: {
    // 动画
}) { finished in
    // 回调
}

我们正在混合使用多个闭包和尾随闭包, animation: 还拥有参数标签, 但 completion: 已经丢掉参数标签变成一个尾随闭包了. 在这种情况下, 我觉得尾随闭包已经跟原有的函数产生了割裂感, 但我猜这是因为 API 的右尖括号跟右括号让我感觉这个函数已经结束了:

}) { finished in // 糟透了

如果你不确定什么是尾随闭包, 我有另一篇文章解释它的定义和用法 Swift: Syntax Cheat Codes

缩进之美

另一个就是 animation 的两个闭包是同一层级的, 而它们默认的缩进却不一致. 最近我感受了一下函数式编程的伟大, 写函数式代码的一个很爽的点在于把那些序列的命令一条一条通过点语法罗列出来:

[0, 1, 2, 4, 5, 6]
    .sorted { $0 < $1 }
    .map { $0 * 2 }
    .forEach { print($0) }

那为什么不能把带两个闭包的 API 用同样的方式列出来?

如果你不理解 $0 语法, 我有另一篇文章介绍如何它们的含义和语法 Swift: Syntax Cheat Codes

把丑陋的语法强制变得优雅

UIView.animate(withDuration: 0.3,
    animations: {
        // 动画
    },
    completion: { finished in
        // 回调
    })

我想借鉴一下函数式编程的语法, 强迫自己去手动调整代码格式而不是用 Xcode 默认的自动补齐. 我个人觉得这样子会让代码可读性更加好但这也是一个很机械性的过程. 每次我复制粘贴这段代码的时候, 缩进总是会乱掉, 但我觉得这是 Xcode 的问题而不是 Swift 的.

传递闭包

let animations = {
    // 动画
}
let completion = { (finished: Bool) in
    // 回调
}
UIView.animate(withDuration: 0.3,
               animations: animations,
               completion: completion)

这篇文章开头我提到闭包是Swift 的一等公民, 这意味着我们可以把它赋值给一个变量并且传递出去. 我觉得这么写并不比上一个例子更具可读性, 而且别的对象只要想要就可以去接触到这些闭包. 如果一定要我选择的话, 我更乐意使用上一种写法.

解决方案

就像许多程序员一样, 我会强迫自己去思考出一个方式去解决这个很常见的问题, 并且告诉自己, 长此以往我可以节省很多时间.

UIView.Animator(duration: 0.3)
    .animations {
        // Animations
    }
    .completion { finished in
        // Completion
    }
    .animate()

就像你看到的, 这种语法和结构从 Swift 函数式的 API 里借鉴了很多. 我们把两个闭包的看作是集合的高等函数, 然后现在代码看起来好很多, 并且在我们换行和复制粘贴的时候, 编译器也会根据我们想要的那样去工作(译者注: 这应该跟 IDE 的 formator 有关, 而不是编译器, 毕竟 Swift 不需要游标卡尺

返回顶部