Pal*_*ndo 56 core-animation ios
我CABasicAnimation在视图中无休止地循环重复图像块:
a = [CABasicAnimation animationWithKeyPath:@"position"];
a.timingFunction = [CAMediaTimingFunction
functionWithName:kCAMediaTimingFunctionLinear];
a.fromValue = [NSValue valueWithCGPoint:CGPointMake(0, 0)];
a.toValue = [NSValue valueWithCGPoint:CGPointMake(image.size.width, 0)];
a.repeatCount = HUGE_VALF;
a.duration = 15.0;
[a retain];
Run Code Online (Sandbox Code Playgroud)
我已尝试按照技术问答QA1673中的描述"暂停并恢复"图层动画.
当应用程序进入后台时,动画将从图层中删除.为了补偿我听UIApplicationDidEnterBackgroundNotification,打电话stopAnimation和回应UIApplicationWillEnterForegroundNotification电话startAnimation.
- (void)startAnimation
{
if ([[self.layer animationKeys] count] == 0)
[self.layer addAnimation:a forKey:@"position"];
CFTimeInterval pausedTime = [self.layer timeOffset];
self.layer.speed = 1.0;
self.layer.timeOffset = 0.0;
self.layer.beginTime = 0.0;
CFTimeInterval timeSincePause =
[self.layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
self.layer.beginTime = timeSincePause;
}
- (void)stopAnimation
{
CFTimeInterval pausedTime =
[self.layer convertTime:CACurrentMediaTime() fromLayer:nil];
self.layer.speed = 0.0;
self.layer.timeOffset = pausedTime;
}
Run Code Online (Sandbox Code Playgroud)
问题是它在开始时再次启动,并且从上一个位置开始出现丑陋的跳跃,如应用程序进入后台时系统所采用的应用程序快照所示,回到动画循环的开始.
当我重新添加动画时,我无法弄清楚如何让它从最后位置开始.坦率地说,我只是不明白QA1673的代码是如何工作的:在resumeLayer它中设置了两次layer.beginTime,这似乎是多余的.但是当我删除第一个设置为零时,它没有恢复暂停的动画.这是通过简单的点击手势识别器测试的,它确实切换了动画 - 这与我从后台恢复的问题没有严格的关系.
在动画被删除之前我应该记住什么状态?当我稍后重新添加动画时,如何从该状态恢复动画?
ccl*_*ogg 51
嘿,我在游戏中偶然发现了同样的事情,最终找到了一个与你不同的解决方案,你可能会喜欢:)我想我应该分享我找到的解决方法......
我的情况是使用UIView/UIImageView动画,但它基本上仍然是CAAnimations的核心...我的方法的要点是我在视图上复制/存储当前动画,然后让Apple的暂停/恢复仍然工作,但之前恢复我重新添加我的动画.那么让我来介绍这个简单的例子:
假设我有一个名为movingView的UIView .UIView的中心通过标准的[ UIView animateWithDuration ... ]调用进行动画制作.使用上面提到的QA1673代码,它可以很好地暂停/恢复(当没有退出应用程序时)......但无论如何,我很快就意识到,在退出时,无论是否暂停,动画都被完全删除了......我在这里在你的位置.
所以在这个例子中,这就是我所做的:
当应用程序退出后台时,我这样做:
animationViewPosition = [[movingView.layer animationForKey:@"position"] copy]; // I know position is the key in this case...
[self pauseLayer:movingView.layer]; // this is the Apple method from QA1673
Run Code Online (Sandbox Code Playgroud)
现在在我的UIApplicationWillEnterForegroundNotification处理程序中:
if (animationViewPosition != nil)
{
[movingView.layer addAnimation:animationViewPosition forKey:@"position"]; // re-add the core animation to the view
[animationViewPosition release]; // since we 'copied' earlier
animationViewPosition = nil;
}
[self resumeLayer:movingView.layer]; // Apple's method, which will resume the animation at the position it was at when the app exited
Run Code Online (Sandbox Code Playgroud)这就是它!它到目前为止对我有用:)
只需为每个动画重复这些步骤,即可轻松扩展它以获得更多动画或视图.它甚至适用于暂停/恢复UIImageView动画,即标准[ imageView startAnimating ].(顺便说一句)的图层动画关键字是"内容".
清单1暂停和恢复动画.
-(void)pauseLayer:(CALayer*)layer
{
CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
layer.speed = 0.0;
layer.timeOffset = pausedTime;
}
-(void)resumeLayer:(CALayer*)layer
{
CFTimeInterval pausedTime = [layer timeOffset];
layer.speed = 1.0;
layer.timeOffset = 0.0;
layer.beginTime = 0.0;
CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
layer.beginTime = timeSincePause;
}
Run Code Online (Sandbox Code Playgroud)
Max*_*eod 18
经过大量的搜索和与iOS开发专家的讨论后,看来QA1673在暂停,后台处理,然后转移到前台时没有帮助.我的实验甚至表明委托从动画中发出的方法,例如animationDidStop变得不可靠.
有时他们会开火,有时他们不会开火.
这会产生很多问题,因为这意味着,您不仅会看到暂停时的不同屏幕,而且还会中断当前运动中的事件序列.
到目前为止我的解决方案如下:
动画开始时,我得到开始时间:
mStartTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
当用户点击暂停按钮时,我从以下位置删除动画CALayer:
[layer removeAnimationForKey:key];
我得到绝对时间使用CACurrentMediaTime():
CFTimeInterval stopTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
使用mStartTime和stopTime我计算一个偏移时间:
mTimeOffset = stopTime - mStartTime;
Run Code Online (Sandbox Code Playgroud)
我还将对象的模型值设置为presentationLayer.所以,我的stop方法看起来像这样:
//--------------------------------------------------------------------------------------------------
- (void)stop
{
const CALayer *presentationLayer = layer.presentationLayer;
layer.bounds = presentationLayer.bounds;
layer.opacity = presentationLayer.opacity;
layer.contentsRect = presentationLayer.contentsRect;
layer.position = presentationLayer.position;
[layer removeAnimationForKey:key];
CFTimeInterval stopTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
mTimeOffset = stopTime - mStartTime;
}
Run Code Online (Sandbox Code Playgroud)
在恢复时,我重新计算基于暂停动画的剩余内容mTimeOffset.这有点乱,因为我正在使用CAKeyframeAnimation.我根据具体情况找出了哪些关键帧是优秀的mTimeOffset.此外,我考虑到暂停可能发生在帧的中间,例如在f1和之间f2.该时间从该关键帧的时间中扣除.
然后我重新将这个动画添加到图层:
[layer addAnimation:animationGroup forKey:key];
另一件需要记住的事情是,您需要检查标志,animationDidStop并仅removeFromSuperlayer在标志为的情况下从父项中删除动画层YES.这意味着在暂停期间图层仍然可见.
这种方法似乎非常费力.它确实有效!我希望能够使用QA1673简单地做到这一点.但目前用于后台处理,它不起作用,这似乎是唯一的解决方案.
Mat*_*ski 14
令人惊讶的是,这并不是那么简单.我根据cclogg的方法创建了一个类别,这应该使它成为一个单行.
CALayer的+ MBAnimationPersistence
MB_setCurrentAnimationsPersistent在设置所需的动画后,只需在您的图层上调用即可.
[movingView.layer MB_setCurrentAnimationsPersistent];
Run Code Online (Sandbox Code Playgroud)
或者指定应该明确持久化的动画.
movingView.layer.MB_persistentAnimationKeys = @[@"position"];
Run Code Online (Sandbox Code Playgroud)
小智 8
我无法评论,所以我会将其添加为答案.
我使用了cclogg的解决方案,但是当动画的视图从他的超级视图中删除,再次添加,然后转到后台时,我的应用程序崩溃了.
通过设置animation.repeatCount,动画变得无限Float.infinity.
我的解决方案是设置animation.removedOnCompletion为false.
它的工作原理非常奇怪,因为动画永远不会完成.如果有人有解释,我喜欢听.
另一个提示:如果从超级视图中删除视图.不要忘记通过调用删除观察者NSNotificationCenter.defaultCenter().removeObserver(...).
我写了一个基于@cclogg和@Matej Bukovinski答案的Swift 4.2版本扩展。您只需要打电话layer.makeAnimationsPersistent()
要点如下:CALayer + AnimationPlayback.swift,CALayer + PersistentAnimations.swift
核心部分:
public extension CALayer {
static private var persistentHelperKey = "CALayer.LayerPersistentHelper"
public func makeAnimationsPersistent() {
var object = objc_getAssociatedObject(self, &CALayer.persistentHelperKey)
if object == nil {
object = LayerPersistentHelper(with: self)
let nonatomic = objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC
objc_setAssociatedObject(self, &CALayer.persistentHelperKey, object, nonatomic)
}
}
}
public class LayerPersistentHelper {
private var persistentAnimations: [String: CAAnimation] = [:]
private var persistentSpeed: Float = 0.0
private weak var layer: CALayer?
public init(with layer: CALayer) {
self.layer = layer
addNotificationObservers()
}
deinit {
removeNotificationObservers()
}
}
private extension LayerPersistentHelper {
func addNotificationObservers() {
let center = NotificationCenter.default
let enterForeground = UIApplication.willEnterForegroundNotification
let enterBackground = UIApplication.didEnterBackgroundNotification
center.addObserver(self, selector: #selector(didBecomeActive), name: enterForeground, object: nil)
center.addObserver(self, selector: #selector(willResignActive), name: enterBackground, object: nil)
}
func removeNotificationObservers() {
NotificationCenter.default.removeObserver(self)
}
func persistAnimations(with keys: [String]?) {
guard let layer = self.layer else { return }
keys?.forEach { (key) in
if let animation = layer.animation(forKey: key) {
persistentAnimations[key] = animation
}
}
}
func restoreAnimations(with keys: [String]?) {
guard let layer = self.layer else { return }
keys?.forEach { (key) in
if let animation = persistentAnimations[key] {
layer.add(animation, forKey: key)
}
}
}
}
@objc extension LayerPersistentHelper {
func didBecomeActive() {
guard let layer = self.layer else { return }
restoreAnimations(with: Array(persistentAnimations.keys))
persistentAnimations.removeAll()
if persistentSpeed == 1.0 { // if layer was playing before background, resume it
layer.resumeAnimations()
}
}
func willResignActive() {
guard let layer = self.layer else { return }
persistentSpeed = layer.speed
layer.speed = 1.0 // in case layer was paused from outside, set speed to 1.0 to get all animations
persistAnimations(with: layer.animationKeys())
layer.speed = persistentSpeed // restore original speed
layer.pauseAnimations()
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
23704 次 |
| 最近记录: |