An animation state machine
UIViewPropertyAnimator 有三个属性可以标记当前动画交互状态:
- isRunning :标记动画是否在运行。默认 false 。
- isReversed :默认 false,动画从其开始状态播放到结束状态。 如果将此属性更改为 true ,则动画将反转方向并回放到其初始状态。
- state :确定动画处于何种活动状态 。
3D touch 交互动画
AnimatorFactory.swift 新增方法:
static func grow(view: UIVisualEffectView, blurView: UIVisualEffectView) -> UIViewPropertyAnimator {
// 1
view.contentView.alpha = 0
view.transform = .identity
// 2
let animator = UIViewPropertyAnimator( duration: 0.5, curve: .easeIn)
// 3
animator.addAnimations {
// blurView.effect = UIBlurEffect(style: UIBlurEffectStyle.dark)
// view.transform = CGAffineTransform(scaleX: 1.5, y: 1.5)
UIView.animateKeyframes(withDuration: 0.5, delay: 0.0, animations: {
UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: 1.0, animations: {
blurView.effect = UIBlurEffect(style: .dark)
view.transform = CGAffineTransform(scaleX: 1.5, y: 1.5)
})
UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.5, animations: {
view.transform = view.transform.rotated(by: -.pi/8)
})
})
}
// 4
animator.addCompletion { position in
switch position {
case .start:
blurView.effect = nil
case .end:
blurView.effect = UIBlurEffect(style: .dark)
default: break
}
}
return animator
}
WidgetsOwnerProtocol.swift
import UIKit
protocol WidgetsOwnerProtocol {
var blurView: UIVisualEffectView {get}
func startPreview(for: UIView)
func updatePreview(percent: CGFloat)
func finishPreview()
func cancelPreview()
}
extension WidgetsOwnerProtocol {
func startPreview(for forView: UIView) { }
func updatePreview(percent: CGFloat) { }
func finishPreview() { }
func cancelPreview() { }
}
LockScreenViewController.swift extension WidgetsOwnerProtocol
extension LockScreenViewController: WidgetsOwnerProtocol {
func startPreview(for forView: UIView) {
previewView?.removeFromSuperview()
previewView = forView.snapshotView(afterScreenUpdates: false)
view.insertSubview(previewView!, aboveSubview: blurView)
previewView?.frame = forView.convert(forView.bounds, to: view)
startFrame = previewView?.frame
addEffectView(below: previewView!)
previewAnimator = AnimatorFactory.grow(view: previewEffectView, blurView: blurView)
}
func updatePreview(percent: CGFloat) {
previewAnimator?.fractionComplete = max(0.01, min(0.99, percent))
}
func cancelPreview() {
if let previewAnimator = previewAnimator {
previewAnimator.isReversed = true
previewAnimator.startAnimation()
previewAnimator.addCompletion { position in
switch position {
case .start:
self.previewView?.removeFromSuperview()
self.previewEffectView.removeFromSuperview()
default: break
}
}
}
}
func finishPreview() {
// 1
previewAnimator?.stopAnimation(false)
// 2
previewAnimator?.finishAnimation(at: .end)
// 3
previewAnimator = nil
AnimatorFactory.complete(view: previewEffectView) .startAnimation()
blurView.effect = UIBlurEffect(style: .dark)
blurView.isUserInteractionEnabled = true
blurView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(dismissMenu)))
}
func addEffectView(below forView: UIView) {
previewEffectView.removeFromSuperview()
previewEffectView.frame = forView.frame
forView.superview?.insertSubview(previewEffectView, belowSubview: forView)
}
@objc func dismissMenu() {
let reset = AnimatorFactory.reset(frame: startFrame!, view: previewEffectView, blurView: blurView)
reset.addCompletion { _ in
self.previewEffectView.removeFromSuperview()
self.previewView?.removeFromSuperview()
self.blurView.isUserInteractionEnabled = false
}
reset.startAnimation()
}
}
网友评论