UIViewPropertyAnimator是在iOS10引入的,它解决了能够创建易于交互,可中断和/或可逆的视图动画的需求。
现在有这样一个场景LockScreenViewController.swift,让中间tableview放大显示
先设置tableView初始状态
override func viewWillAppear(_ animated: Bool) {
tableView.transform = CGAffineTransform(scaleX: 0.67, y: 0.67)
tableView.alpha = 0
}
此时tableView已经被隐藏,然后在viewDidAppear中添加动画
override func viewDidAppear(_ animated: Bool) {
let scale = UIViewPropertyAnimator(duration: 0.33, curve: .easeIn)
scale.addAnimations {
self.tableView.alpha = 1.0
}
scale.addAnimations({
self.tableView.transform = .identity
}, delayFactor: 0.33)
scale.addCompletion { (_) in
print("ready")
}
scale.startAnimation()
}
这里要介绍一下delayFactor,它并不是我们前面学习到的delay延迟多少秒,它是延迟剩余时间的百分比执行,取值在0.0-1.0,什么意思呢?比如动画总时长为3秒,已经执行了1秒,还有2秒未执行,如果delayFactor是0.33,那么动画将在此刻延迟2*0.33秒执行。
你可以在每个addAnimations(_:)后添加scale.addCompletion,那么动画将一个接一个的执行。
最后我们需要启动动画
scale.startAnimation()
运行效果
我们将代码封装一下,创建新的类AnimatorFactory.swift
import UIKit
class AnimatorFactory {
static func scaleUp(view: UIView) -> UIViewPropertyAnimator {
let scale = UIViewPropertyAnimator(duration: 0.33,
curve: .easeIn)
scale.addAnimations {
view.alpha = 1.0
}
scale.addAnimations({
view.transform = CGAffineTransform.identity
}, delayFactor: 0.33)
scale.addCompletion {_ in
print("ready")
}
return scale
}
}
替换viewDidLoad
override func viewDidAppear(_ animated: Bool) {
AnimatorFactory.scaleUp(view: tableView).startAnimation()
}
如果是运行单个动画而不需要交互,那么你可以继续使用之前学习的UIView.animate(withDuration:…),当然UIViewPropertyAnimator也有对应的动画API。
在LockScreenViewController.swift中添加代码:
func toggleBlur(_ blurred: Bool) {
UIViewPropertyAnimator.runningPropertyAnimator(withDuration: 0.5, delay: 0.1, options: .curveEaseOut, animations: {
self.blurView.alpha = blurred ? 1 : 0
}, completion: nil)
}
在UISearchBarDelegate代理方法中调用
extension LockScreenViewController: UISearchBarDelegate {
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
toggleBlur(true)
}
func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
toggleBlur(false)
}
func searchBarResultsListButtonClicked(_ searchBar: UISearchBar) {
searchBar.resignFirstResponder()
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchText.isEmpty {
searchBar.resignFirstResponder()
}
}
}
运行效果
控制关键帧动画
继续使用AnimatorFactory.swift,添加新的方法:
@discardableResult
static func jiggle(view: UIView) -> UIViewPropertyAnimator {
return UIViewPropertyAnimator.runningPropertyAnimator(withDuration: 0.33, delay: 0, animations: {
UIView.animateKeyframes(withDuration: 1, delay: 0, animations: {
UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: 0.25, animations: {
view.transform = CGAffineTransform(rotationAngle: -.pi/8)
})
UIView.addKeyframe(withRelativeStartTime: 0.25, relativeDuration: 0.75, animations: {
view.transform = CGAffineTransform(rotationAngle: +.pi/8)
})
UIView.addKeyframe(withRelativeStartTime: 0.75, relativeDuration: 1.0, animations: {
view.transform = CGAffineTransform.identity
})
}, completion: nil)
}, completion: { (_) in
view.transform = .identity
})
}
当在外层添加UIViewPropertyAnimator的方法时,会中断或者延长UIView.animateKeyframes的执行时间。