《iOS 10 day by day》是 shinobicontrols 公司编写的系列博客,介绍开发者需要了解的 iOS 10 新特性,每周更新。本系列翻译(文集地址)已取得官方授权。目录点此。仓薯翻译,欢迎指正:)
Shinobicontrols 为 iOS 和 Android 开发者提供高性能、响应式的 UI 控件 SDK,尤其是图表方面的控件。 官网 : shinobicontrols.com twitter : @shinobicontrols
曾经的黑暗年代
用基于 block 的 UIView animation 来编写 view 属性(frame, transform 等等)变化的动画非常简单。只需要短短几行代码:
1 2 3 4 5 |
view.alpha = 1 UIView.animate(withDuration: 2) { containerView.alpha = 0 } |
你可以指定动画结束之后调用的 completion block。如果默认的匀速动画不能满足你的要求,还可以调整时间曲线。
但是,如果你需要一种自定义的曲线动画,相应的属性变化首先要快速开始,然后再急速慢下来,该怎么办呢?另外一个有点麻烦的问题是,怎么取消正在进行中的动画?虽然这些问题都可以解决,用第三方库或者创建一个新的 animation 来取代进行中的 animation。但苹果在 UIKit 中新加的组件能把这些步骤简化许多:进入UIViewPropertyAnimator
的世界吧!
Animation 的新纪元
UIViewPropertyAnimator
的 API 设计得很完善,可扩展性也很好。它 cover 了传统 UIView animation 动画的绝大部分功能,并且大大增强了你对动画过程的掌控能力。具体来说,你可以在动画过程中任意时刻暂停,可以随后再选择继续,甚至还能在动画过程中动态改变动画的属性(例如,本来动画终点在屏幕左下角的,可以在动画过程中把终点改到右上角)。
为了探索这个新的类,我们来看几个例子,这几个例子都是演示一张图片划过屏幕的动画。如同所有 Day by Day 系列的文章,例子的代码可以在 Github 上下载到。这次我们用的是 Playground。
Playground 的准备
我们所有的 playground 页面都是让一个小忍者划过屏幕的动画。为了方便对比这些页面的代码,我们把公共部分的代码藏在 Sources
文件夹里。这样不仅能简化每个页面的代码,还能加快编译过程,因为 Sources
里的代码是预编译过的。
Sources
里包含一个简单的UIView
子类,叫做NinjaContainerView
。它的唯一功能就是添加一个 UIImageView
作为子 view,来显示我们的小忍者。我把忍者图片加到了 Resources
里。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
import UIKit public class NinjaContainerView: UIView { public let ninja: UIImageView = { let image = UIImage(named: "ninja") let view = UIImageView(image: image) view.frame = CGRect(x: 0, y: 0, width: 45, height: 39) return view }() public override init(frame: CGRect) { // Animating view super.init(frame: frame) // Position ninja in the bottom left of the view ninja.center = { let x = (frame.minX + ninja.frame.width / 2) let y = (frame.maxY - ninja.frame.height / 2) return CGPoint(x: x, y: y) }() // Add image to the container addSubview(ninja) backgroundColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1) } required public init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } /// Moves the ninja view to the bottom right of its container, positioned just inside. public func moveNinjaToBottomRight() { ninja.center = { let x = (frame.maxX - ninja.frame.width / 2) let y = (frame.maxY - ninja.frame.height / 2) return CGPoint(x: x, y: y) }() } } |
现在,在每个 playground 页面里,我们可以复制粘贴以下代码:
1 2 3 4 5 6 7 8 9 10 |
import UIKit import PlaygroundSupport // Container for our animating view let containerView = NinjaContainerView(frame: CGRect(x: 0, y: 0, width: 400, height: 400)) let ninja = containerView.ninja // Show the container view in the Assistant Editor PlaygroundPage.current.liveView = containerView |
这样我们就可以用上 Playground 强大的 “Live View” 功能,不用启动 i blank">文集地址)已取得官方授权。目录点此。仓薯翻译,欢迎指正:)
Shinobicontrols 为 iOS 和 Android 开发者提供高性能、响应式的 UI 控件 SDK,尤其是图表方面的控件。 官网 : shinobicontrols.com twitter : @shinobicontrols
曾经的黑暗年代
用基于 block 的 UIView animation 来编写 view 属性(frame, transform 等等)变化的动画非常简单。只需要短短几行代码:
1 2 3 4 5 |
view.alpha = 1 UIView.animate(withDuration: 2) { containerView.alpha = 0 } |
你可以指定动画结束之后调用的 completion block。如果默认的匀速动画不能满足你的要求,还可以调整时间曲线。
但是,如果你需要一种自定义的曲线动画,相应的属性变化首先要快速开始,然后再急速慢下来,该怎么办呢?另外一个有点麻烦的问题是,怎么取消正在进行中的动画?虽然这些问题都可以解决,用第三方库或者创建一个新的 animation 来取代进行中的 animation。但苹果在 UIKit 中新加的组件能把这些步骤简化许多:进入UIViewPropertyAnimator
的世界吧!
Animation 的新纪元
UIViewPropertyAnimator
的 API 设计得很完善,可扩展性也很好。它 cover 了传统 UIView animation 动画的绝大部分功能,并且大大增强了你对动画过程的掌控能力。具体来说,你可以在动画过程中任意时刻暂停,可以随后再选择继续,甚至还能在动画过程中动态改变动画的属性(例如,本来动画终点在屏幕左下角的,可以在动画过程中把终点改到右上角)。
为了探索这个新的类,我们来看几个例子,这几个例子都是演示一张图片划过屏幕的动画。如同所有 Day by Day 系列的文章,例子的代码可以在 Github 上下载到。这次我们用的是 Playground。
Playground 的准备
我们所有的 playground 页面都是让一个小忍者划过屏幕的动画。为了方便对比这些页面的代码,我们把公共部分的代码藏在 Sources
文件夹里。这样不仅能简化每个页面的代码,还能加快编译过程,因为 Sources
里的代码是预编译过的。
Sources
里包含一个简单的UIView
子类,叫做NinjaContainerView
。它的唯一功能就是添加一个 UIImageView
作为子 view,来显示我们的小忍者。我把忍者图片加到了 Resources
里。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
import UIKit public class NinjaContainerView: UIView { public let ninja: UIImageView = { let image = UIImage(named: "ninja") let view = UIImageView(image: image) view.frame = CGRect(x: 0, y: 0, width: 45, height: 39) return view }() public override init(frame: CGRect) { // Animating view super.init(frame: frame) // Position ninja in the bottom left of the view ninja.center = { let x = (frame.minX + ninja.frame.width / 2) let y = (frame.maxY - ninja.frame.height / 2) return CGPoint(x: x, y: y) }() // Add image to the container addSubview(ninja) backgroundColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1) } required public init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } /// Moves the ninja view to the bottom right of its container, positioned just inside. public func moveNinjaToBottomRight() { ninja.center = { let x = (frame.maxX - ninja.frame.width / 2) let y = (frame.maxY - ninja.frame.height / 2) return CGPoint(x: x, y: y) }() } } |
现在,在每个 playground 页面里,我们可以复制粘贴以下代码:
1 2 3 4 5 6 7 8 9 10 |
import UIKit import PlaygroundSupport // Container for our animating view let containerView = NinjaContainerView(frame: CGRect(x: 0, y: 0, width: 400, height: 400)) let ninja = containerView.ninja // Show the container view in the Assistant Editor PlaygroundPage.current.liveView = containerView |
这样我们就可以用上 Playground 强大的 “Live View” 功能,不用启动 i = " c r a y o n - o " > = / s p a n > s p a n c l a s s = " c r a y o n - h " > / s p a n > s p a n c l a s s = " c r a y o n - c n " > 0 / s p a n > s p a n c l a s s = " c r a y o n - s y " > ; / s p a n > / d i v > d i v c l a s s = " c r a y o n - l i n e c r a y o n - s t r i p e d - l i n e " i d = " c r a y o n - 5 8 1 2 c b 7 f 8 4 6 8 8 8 6 8 6 7 3 2 9 9 - 2 6 " >