回答:
これが答えです。私の答えとクリシュナンの組み合わせです。
cabasicanimation.fillMode = kCAFillModeForwards;
cabasicanimation.removedOnCompletion = NO;
デフォルト値はkCAFillModeRemoved
です。(これは、表示されているリセット動作です。)
deletedOnCompletionの問題は、UI要素がユーザーの操作を許可しないことです。
私のテクニックは、アニメーションのFROM値とオブジェクトのTO値を設定することです。アニメーションは、開始前にTO値を自動入力し、削除されると、オブジェクトを正しい状態のままにします。
// fade in
CABasicAnimation *alphaAnimation = [CABasicAnimation animationWithKeyPath: @"opacity"];
alphaAnimation.fillMode = kCAFillModeForwards;
alphaAnimation.fromValue = NUM_FLOAT(0);
self.view.layer.opacity = 1;
[self.view.layer addAnimation: alphaAnimation forKey: @"fade"];
alphaAnimation.beginTime = 1;
また、fillMode
私が知る限り、これは必要ありません...?
beginTime
相対的ではありません、あなたが使用する必要があります。例:CACurrentMediaTime()+1;
fillMode
ではありません。明示的なCAAnimationsを使用するときにレイヤーが元の値に
次のプロパティを設定します。
animationObject.removedOnCompletion = NO;
あなたのコードの中に入れてください
CAAnimationGroup *theGroup = [CAAnimationGroup animation];
theGroup.fillMode = kCAFillModeForwards;
theGroup.removedOnCompletion = NO;
レイヤーに追加CABasicAnimation
するposition
ときに、のキーを設定するだけです。これにより、実行ループの現在のパスの位置で行われた暗黙的なアニメーションが上書きされます。
CGFloat yOffset = 30;
CGPoint endPosition = CGPointMake(someLayer.position.x,someLayer.position.y + yOffset);
someLayer.position = endPosition; // Implicit animation for position
CABasicAnimation * animation =[CABasicAnimation animationWithKeyPath:@"position.y"];
animation.fromValue = @(someLayer.position.y);
animation.toValue = @(someLayer.position.y + yOffset);
[someLayer addAnimation:animation forKey:@"position"]; // The explicit animation 'animation' override implicit animation
2011 Apple WWDC Video Session 421-Core Animation Essentials(ビデオの中央)に関する詳細情報があります。
someLayer.position = endPosition;
。そして、WWDCへの参照をありがとう!
CALayerには、モデルレイヤーとプレゼンテーションレイヤーがあります。アニメーション中、プレゼンテーションレイヤーはモデルとは関係なく更新されます。アニメーションが完了すると、プレゼンテーションレイヤーがモデルの値で更新されます。アニメーションが終了した後に不快なジャンプを避けたい場合は、2つのレイヤーの同期を保つことが重要です。
最終値がわかっている場合は、モデルを直接設定できます。
self.view.layer.opacity = 1;
ただし、終了位置がわからないアニメーション(たとえば、ユーザーが一時停止してから戻すことができるゆっくりとしたフェード)がある場合は、プレゼンテーションレイヤーに直接クエリを実行して現在の値を確認し、モデルを更新できます。
NSNumber *opacity = [self.layer.presentationLayer valueForKeyPath:@"opacity"];
[self.layer setValue:opacity forKeyPath:@"opacity"];
プレゼンテーションレイヤーから値を取得することは、キーパスのスケーリングや回転にも特に役立ちます。(例transform.scale
、transform.rotation
)
を使用せずに removedOnCompletion
あなたはこのテクニックを試すことができます:
self.animateOnX(item: shapeLayer)
func animateOnX(item:CAShapeLayer)
{
let endPostion = CGPoint(x: 200, y: 0)
let pathAnimation = CABasicAnimation(keyPath: "position")
//
pathAnimation.duration = 20
pathAnimation.fromValue = CGPoint(x: 0, y: 0)//comment this line and notice the difference
pathAnimation.toValue = endPostion
pathAnimation.fillMode = kCAFillModeBoth
item.position = endPostion//prevent the CABasicAnimation from resetting item's position when the animation finishes
item.add(pathAnimation, forKey: nil)
}
したがって、私の問題は、パンジェスチャーでオブジェクトを回転させようとしていたことで、1回の移動で複数の同一のアニメーションが発生しました。私は両方を持っていたfillMode = kCAFillModeForwards
し、isRemovedOnCompletion = false
それは助けにはなりませんでした。私の場合、新しいアニメーションを追加するたびにアニメーションキーが異なることを確認する必要がありました。
let angle = // here is my computed angle
let rotate = CABasicAnimation(keyPath: "transform.rotation.z")
rotate.toValue = angle
rotate.duration = 0.1
rotate.isRemovedOnCompletion = false
rotate.fillMode = CAMediaTimingFillMode.forwards
head.layer.add(rotate, forKey: "rotate\(angle)")
単に設定しfillMode
、removedOnCompletion
私のために動作しませんでした。以下のすべてのプロパティをCABasicAnimationオブジェクトに設定することで問題を解決しました。
CABasicAnimation* ba = [CABasicAnimation animationWithKeyPath:@"transform"];
ba.duration = 0.38f;
ba.fillMode = kCAFillModeForwards;
ba.removedOnCompletion = NO;
ba.autoreverses = NO;
ba.repeatCount = 0;
ba.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.85f, 0.85f, 1.0f)];
[myView.layer addAnimation:ba forKey:nil];
このコードmyView
は、サイズの85%に変換されます(3次元は変更されません)。
コアアニメーションは、モデルレイヤーとプレゼンテーションレイヤーという 2つのレイヤー階層を維持します。アニメーションの進行中、モデルレイヤーは実際にはそのままで、初期値を維持します。デフォルトでは、アニメーションは完了すると削除されます。次に、プレゼンテーションレイヤーはモデルレイヤーの値にフォールバックします。
単にに設定removedOnCompletion
するNO
と、アニメーションが削除されず、メモリが浪費されます。さらに、モデルレイヤーとプレゼンテーションレイヤーが同期しなくなるため、バグが発生する可能性があります。
そのため、モデルレイヤーのプロパティを直接最終的な値に更新する方が良い解決策になります。
self.view.layer.opacity = 1;
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
animation.fromValue = 0;
animation.toValue = 1;
[self.view.layer addAnimation:animation forKey:nil];
上記のコードの最初の行によって引き起こされた暗黙のアニメーションがある場合は、オフにしてみてください。
[CATransaction begin];
[CATransaction setDisableActions:YES];
self.view.layer.opacity = 1;
[CATransaction commit];
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
animation.fromValue = 0;
animation.toValue = 1;
[self.view.layer addAnimation:animation forKey:nil];
これは機能します:
let animation = CABasicAnimation(keyPath: "opacity")
animation.fromValue = 0
animation.toValue = 1
animation.duration = 0.3
someLayer.opacity = 1 // important, this is the state you want visible after the animation finishes.
someLayer.addAnimation(animation, forKey: "myAnimation")
コアアニメーションは、アニメーション中に通常のレイヤーの上に「プレゼンテーションレイヤー」を表示します。そのため、アニメーションが終了してプレゼンテーションレイヤーが消えたときに表示する不透明度(または何でも)を設定します。アニメーションを追加する前に、この行でこれを実行して、完了時にちらつきを回避します。
遅延させたい場合は、次のようにします。
let animation = CABasicAnimation(keyPath: "opacity")
animation.fromValue = 0
animation.toValue = 1
animation.duration = 0.3
animation.beginTime = someLayer.convertTime(CACurrentMediaTime(), fromLayer: nil) + 1
animation.fillMode = kCAFillModeBackwards // So the opacity is 0 while the animation waits to start.
someLayer.opacity = 1 // <- important, this is the state you want visible after the animation finishes.
someLayer.addAnimation(animation, forKey: "myAnimation")
最後に、「removedOnCompletion = false」を使用すると、最終的にレイヤーが破棄されるまでCAAnimationsがリークされます-回避してください。
@Leslie Godwinの答えはあまり良くありません、「self.view.layer.opacity = 1;」すぐに完了します(約1秒かかります)。疑問がある場合は、alphaAnimation.durationを10.0に修正してください。この行を削除する必要があります。
したがって、fillModeをkCAFillModeForwardsに修正し、removeOnCompletionをNOに修正すると、アニメーションをレイヤーに残すことができます。アニメーションデリゲートを修正して、次のようなことを試してみてください。
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
{
[theLayer removeAllAnimations];
}
...この行を実行すると、レイヤーはすぐに復元されます。それは私たちが避けたかったものです。
アニメーションを削除する前に、レイヤープロパティを修正する必要があります。これを試して:
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
{
if([anim isKindOfClass:[CABasicAnimation class] ]) // check, because of the cast
{
CALayer *theLayer = 0;
if(anim==[_b1 animationForKey:@"opacity"])
theLayer = _b1; // I have two layers
else
if(anim==[_b2 animationForKey:@"opacity"])
theLayer = _b2;
if(theLayer)
{
CGFloat toValue = [((CABasicAnimation*)anim).toValue floatValue];
[theLayer setOpacity:toValue];
[theLayer removeAllAnimations];
}
}
}
また、removeOnCompletionフラグをfalseに設定し、fillModeをkCAFillModeForwardsに設定しても機能しません。
レイヤーに新しいアニメーションを適用すると、アニメーションオブジェクトが初期状態にリセットされ、その状態からアニメーションします。さらに行う必要があるのは、次のように新しいアニメーションを設定する前に、プレゼンテーションレイヤーのプロパティに従ってモデルレイヤーの必要なプロパティを設定することです。
someLayer.path = ((CAShapeLayer *)[someLayer presentationLayer]).path;
[someLayer addAnimation:someAnimation forKey:@"someAnimation"];
最も簡単な解決策は、暗黙のアニメーションを使用することです。これはあなたのためにそのトラブルのすべてを処理します:
self.layer?.backgroundColor = NSColor.red.cgColor;
たとえば期間をカスタマイズしたい場合は、以下を使用できますNSAnimationContext
。
NSAnimationContext.beginGrouping();
NSAnimationContext.current.duration = 0.5;
self.layer?.backgroundColor = NSColor.red.cgColor;
NSAnimationContext.endGrouping();
注:これはmacOSでのみテストされています。
これを行うと、最初はアニメーションが表示されませんでした。問題は、ビュー付きレイヤーのレイヤーが暗黙的にアニメーション化しないことです。これを解決するには、自分でレイヤーを追加してください(ビューをレイヤーバッキングに設定する前に)。
これを行う方法の例は次のとおりです。
override func awakeFromNib() {
self.layer = CALayer();
//self.wantsLayer = true;
}
を使用self.wantsLayer
してもテストに違いはありませんでしたが、知らない副作用が発生する可能性があります。
遊び場からのサンプルです:
import PlaygroundSupport
import UIKit
let resultRotation = CGFloat.pi / 2
let view = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 200.0, height: 300.0))
view.backgroundColor = .red
//--------------------------------------------------------------------------------
let rotate = CABasicAnimation(keyPath: "transform.rotation.z") // 1
rotate.fromValue = CGFloat.pi / 3 // 2
rotate.toValue = resultRotation // 3
rotate.duration = 5.0 // 4
rotate.beginTime = CACurrentMediaTime() + 1.0 // 5
// rotate.isRemovedOnCompletion = false // 6
rotate.fillMode = .backwards // 7
view.layer.add(rotate, forKey: nil) // 8
view.layer.setAffineTransform(CGAffineTransform(rotationAngle: resultRotation)) // 9
//--------------------------------------------------------------------------------
PlaygroundPage.current.liveView = view
false
にisRemovedOnCompletion
-アニメーションが終了した後のCore Animationがきれいましょう
.presentation()
、「最終的な、見られた」値を得ます。プレゼンテーション層で行われたことを説明する以下の正しい答えを探してください。