使用来自 gist 的测试代码,我已对其进行了更新以检查动画,因此无需阅读它。您可以使用唯一 ID 来跟踪所有动画并将其存储在具有视图属性的字典中。我没有实现这部分,但我就是这样做的。希望我足够理解你的问题。我也使用了 Xcode 9,但我不确定代码差异。我更改了一些逻辑部分,所以让我知道这是否解决了问题。
UUID().uuidString //for unique string in implementation
//from your code just slightly altered.
//
// ViewController.swift
// CATest
//
// CATestViewController.swift
// SimpleCALayerTest
import UIKit
class CATestViewController: UIViewController, CAAnimationDelegate{
var slider : UISlider!
var animatableLayer : CALayer!
var animationContainerView : UIView!
var centerY : CGFloat!
var startTranslationX : CGFloat!
var endTranslationX : CGFloat!
let duration = 10.0
///boring nibless view setup code
override func loadView() {
let marginX = CGFloat(10)
let marginY = CGFloat(10)
let view = UIView(frame: CGRect(x: 0, y: 0, width: 400, height: 400))
view.backgroundColor = .lightGray
slider = UISlider(frame: CGRect(x: marginX, y: 0, width: 200, height: 50))
slider.maximumValue = Float(duration)
slider.addTarget(self, action: #selector(sliderChanged(_:)), for: .valueChanged)
slider.addTarget(self, action: #selector(sliderDragStart(_:)), for: .touchDown)
slider.addTarget(self, action: #selector(sliderDragEnd(_:)), for: .touchUpInside)
//A view to house an animated sublayer
animationContainerView = UIView(frame: CGRect(x: marginX, y: 50, width: 200, height: 70))
//add a play button that will allow the animation to be played without hindrance from the slider
let playButton = UIButton(frame: CGRect(x: marginX, y: animationContainerView.frame.maxY + marginY, width: 200, height: 50))
playButton.setTitle("Change Frame", for: .normal)
playButton.addTarget(self, action: #selector(playAnimation), for: .touchUpInside)
view.addSubview(playButton)
//add a stopped ani button that will allow the animation to be played using slider
let addStoppedAniButton = UIButton(frame: CGRect(x: playButton.frame.origin.x, y: playButton.frame.maxY + marginY, width: playButton.frame.width, height: playButton.frame.size.height))
addStoppedAniButton.setTitle("Pause", for: .normal)
addStoppedAniButton.addTarget(self, action: #selector(cmPauseTapped(_:)), for: .touchUpInside)
view.addSubview(addStoppedAniButton)
let animatableLayerWidth = animationContainerView.bounds.width / CGFloat(4)
centerY = animationContainerView.bounds.midY
startTranslationX = animatableLayerWidth / CGFloat(2)
endTranslationX = animationContainerView.bounds.width - animatableLayerWidth / CGFloat(2)
animationContainerView.backgroundColor = .white
animationContainerView.layer.borderColor = UIColor.black.withAlphaComponent(0.5).cgColor
animationContainerView.layer.borderWidth = 1
view.addSubview(slider)
view.addSubview(animationContainerView)
//Now add a layer to animate to the container
animatableLayer = CALayer()
animatableLayer.backgroundColor = UIColor.yellow.cgColor
animatableLayer.borderWidth = 1
animatableLayer.borderColor = UIColor.black.withAlphaComponent(0.5).cgColor
var r = animationContainerView.bounds.insetBy(dx: 0, dy: 4)
r.size.width = animatableLayerWidth
animatableLayer.frame = r
animationContainerView.layer.addSublayer(animatableLayer)
self.view = view
}
@objc func cmPauseTapped(_ sender : UIButton){
if animatableLayer.speed == 0{
resume()
}else{
pause()
}
}
@objc func sliderChanged(_ sender: UISlider){
if animatableLayer.speed == 0{
let time = CFTimeInterval(sender.value)
animatableLayer.speed = 0
animatableLayer.timeOffset = time// offset
print("Time changed \(time)")
}
}
var animations = [CAAnimation]()
func addAnimations(){
let ani = CAAnimation()
animations.append(ani)
}
@objc func sliderDragStart(_ sender: UISlider)
{
if animatableLayer.speed > 0{
animatableLayer.speed = 0
}
addStoppedAnimation()
}
func pause(){
//just updating slider
if slider.value != Float(animatableLayer.timeOffset){
UIView.animate(withDuration: 0.3, animations: {
self.slider.setValue(Float(self.animatableLayer.timeOffset), animated: true)
})
}
animatableLayer.timeOffset = animatableLayer.convertTime(CACurrentMediaTime(), from: nil)
animatableLayer.speed = 0
}
func resume(){
if let _ = animatableLayer.animationKeys()?.contains("an animation key"){
animatableLayer.speed = 1.0;
let pausedTime = animatableLayer.timeOffset
animatableLayer.beginTime = 0.0;
let timeSincePause = animatableLayer.convertTime(CACurrentMediaTime(), from: nil) - pausedTime
animatableLayer.beginTime = timeSincePause;
return
}
print("Drag End with need to readd animation")
ani = createGroup()
animatableLayer.speed = 1
animatableLayer.add(ani, forKey: "an animation key")
let time = CFTimeInterval(slider.value)
animatableLayer.timeOffset = time
animatableLayer.beginTime = CACurrentMediaTime()
}
@objc func sliderDragEnd(_ sender: UISlider){
resume()
}
//Animations
var ani : CAAnimationGroup!
func createGroup() -> CAAnimationGroup
{
let ani = CAAnimationGroup()
ani.isRemovedOnCompletion = false
ani.duration = 10
ani.delegate = self
ani.animations = [createTranslationAnimation(),createColourAnimation()]
return ani
}
func createTranslationAnimation() -> CAKeyframeAnimation
{
let ani = CAKeyframeAnimation(keyPath: "position")
ani.delegate = self
ani.isRemovedOnCompletion = false
ani.duration = 10
ani.values = [CGPoint(x:0,y:centerY),CGPoint(x:endTranslationX,y:centerY)]
ani.keyTimes = [0,1]
return ani
}
func createColourAnimation() -> CAKeyframeAnimation
{
let ani = CAKeyframeAnimation(keyPath: "backgroundColor")
ani.delegate = self
ani.isRemovedOnCompletion = false
ani.duration = 10
ani.values = [UIColor.red.cgColor,UIColor.blue.cgColor]
ani.keyTimes = [0,1]
return ani
}
func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
print("Animation Stopped")
}
func animationDidStart(_ anim: CAAnimation) {
print("Animation started")
}
func addStoppedAnimation()
{
if let _ = animatableLayer.animationKeys()?.contains("an animation key"){
slider.value += 0.5
sliderChanged(slider)
return
//we do not want to readd it
}
ani = createGroup()
animatableLayer.speed = 0
animatableLayer.add(ani, forKey: "an animation key")
let time = CFTimeInterval(slider.value)
animatableLayer.timeOffset = time
animatableLayer.beginTime = CACurrentMediaTime()
}
@objc func playAnimation(){
addStoppedAnimation()
}
}