-drawInContext:メソッドにいくつかの複雑な描画コードを含むレイヤーがあります。必要な描画の量を最小限にしようとしているので、-setNeedsDisplayInRect:を使用して、変更された部分だけを更新しています。これは見事に機能しています。ただし、グラフィックシステムがレイヤーを更新すると、クロスフェードを使用して古いイメージから新しいイメージに移行します。すぐに切り替えてほしい。
CATransactionを使用してアクションをオフにし、期間をゼロに設定しようとしましたが、どちらも機能しませんでした。私が使用しているコードは次のとおりです。
[CATransaction begin];
[CATransaction setDisableActions: YES];
[self setNeedsDisplayInRect: rect];
[CATransaction commit];
代わりに使用する必要のあるCATransactionに別のメソッドがありますか(kCATransactionDisableActionsで-setValue:forKey:も同じ結果を試しました)。
これを行うには、レイヤー上のアクションディクショナリを設定して[NSNull null]
適切なキーのアニメーションとして。たとえば、私は
NSDictionary *newActions = @{
@"onOrderIn": [NSNull null],
@"onOrderOut": [NSNull null],
@"sublayers": [NSNull null],
@"contents": [NSNull null],
@"bounds": [NSNull null]
};
layer.actions = newActions;
レイヤー内のサブレイヤーの挿入または変更時のフェードイン/アウトアニメーション、およびレイヤーのサイズとコンテンツの変更を無効にします。 contents
キーは、更新された図面のクロスフェードを防ぐために探しているキーだと思います。
Swiftバージョン:
let newActions = [
"onOrderIn": NSNull(),
"onOrderOut": NSNull(),
"sublayers": NSNull(),
"contents": NSNull(),
"bounds": NSNull(),
]
また:
[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
//foo
[CATransaction commit];
レイヤーのプロパティを変更すると、通常、CAは暗黙的なトランザクションオブジェクトを作成して変更をアニメーション化します。変更をアニメートしたくない場合は、明示的なトランザクションを作成し、そのkCATransactionDisableActionsプロパティをtrueに設定することにより、暗黙的なアニメーションを無効にできます。
Objective-C
[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
// change properties here without animation
[CATransaction commit];
Swift
CATransaction.begin()
CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions)
// change properties here without animation
CATransaction.commit()
Brad Larson's answer :に加えて、カスタムレイヤー(ユーザーが作成したもの)の場合 委任を使用できます レイヤーのactions
辞書を変更する代わりに。このアプローチはより動的で、よりパフォーマンスが高い場合があります。また、アニメート可能なすべてのキーをリストすることなく、すべての暗黙的なアニメーションを無効にすることができます。
残念ながら、各UIView
は既に独自のレイヤーのデリゲートであるため、UIView
sをカスタムレイヤーデリゲートとして使用することはできません。ただし、次のような単純なヘルパークラスを使用できます。
@interface MyLayerDelegate : NSObject
@property (nonatomic, assign) BOOL disableImplicitAnimations;
@end
@implementation MyLayerDelegate
- (id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event
{
if (self.disableImplicitAnimations)
return (id)[NSNull null]; // disable all implicit animations
else return nil; // allow implicit animations
// you can also test specific key names; for example, to disable bounds animation:
// if ([event isEqualToString:@"bounds"]) return (id)[NSNull null];
}
@end
使用法(ビュー内):
MyLayerDelegate *delegate = [[MyLayerDelegate alloc] init];
// assign to a strong property, because CALayer's "delegate" property is weak
self.myLayerDelegate = delegate;
self.myLayer = [CALayer layer];
self.myLayer.delegate = delegate;
// ...
self.myLayerDelegate.disableImplicitAnimations = YES;
self.myLayer.position = (CGPoint){.x = 10, .y = 42}; // will not animate
// ...
self.myLayerDelegate.disableImplicitAnimations = NO;
self.myLayer.position = (CGPoint){.x = 0, .y = 0}; // will animate
ビューのカスタムサブレイヤーのデリゲートとしてビューのコントローラーを使用すると便利な場合があります。この場合、ヘルパークラスは必要ありません。actionForLayer:forKey:
コントローラー内のメソッド。
重要な注意:UIView
の基になるレイヤーのデリゲートを変更しようとしないでください(たとえば、暗黙的なアニメーションを有効にするため)—悪いことが起こります:)
注:レイヤーの再描画をアニメーション化する(アニメーションを無効にしない)場合は、[CALayer setNeedsDisplayInRect:]
は、CATransaction
内で呼び出します。これは、実際の再描画が後で行われる可能性があるため(おそらくそうなる可能性があるため)です。 この回答では のように、カスタムプロパティを使用することをお勧めします。
受け入れられた答えに似ていますが、Swiftに対するより効率的なソリューションがあります。場合によっては、値を変更するたびにトランザクションを作成するよりも優れている場合があります。 60fpsでレイヤーの位置をドラッグする一般的な使用例。
// Disable implicit position animation.
layer.actions = ["position": NSNull()]
レイヤーアクションの解決方法 については、Appleのドキュメントを参照してください。デリゲートを実装すると、カスケードのもう1つのレベルがスキップされますが、私の場合は、 関連するUIViewに設定する必要があるデリゲートに関する注意 のために面倒です。
編集:NSNull
がCAAction
に準拠していることを指摘しているコメント者のおかげで更新されました。
実際、私は正しい答えを見つけることができませんでした。私のために問題を解決する方法はこれでした:
- (id<CAAction>)actionForKey:(NSString *)event {
return nil;
}
その後、特定のアニメーションを無効にするために、その中の任意のロジックを使用できますが、それらをすべて削除したかったので、nilを返しました。
サムの答えとサイモンの難しさに基づいて... CSShapeLayerを作成した後、デリゲート参照を追加します。
CAShapeLayer *myLayer = [CAShapeLayer layer];
myLayer.delegate = self; // <- set delegate here, it's magic.
...「m」ファイルの他の場所...
基本的に、カスタムの「disableImplicitAnimations」変数配置を介して切り替える機能のないサムと同じです。 「ハードワイヤー」アプローチの詳細。
- (id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event {
// disable all implicit animations
return (id)[NSNull null];
// allow implicit animations
// return nil;
// you can also test specific key names; for example, to disable bounds animation:
// if ([event isEqualToString:@"bounds"]) return (id)[NSNull null];
}
CATransaction
キーに対してsetValue:forKey:
を内部的に呼び出すkCATransactionDisableActions
内のアクションを無効にする簡単なメソッドを見つけました。
[CATransaction setDisableActions:YES];
迅速:
CATransaction.setDisableActions(true)
Swiftで暗黙的なレイヤーアニメーションを無効にするには
CATransaction.setDisableActions(true)
-drawRect()メソッドを実装するカスタムクラスにこれを追加します。ニーズに合わせてコードを変更します。私にとっては、「不透明度」がクロスフェードアニメーションを停止するトリックを行いました。
-(id<CAAction>) actionForLayer:(CALayer *)layer forKey:(NSString *)key
{
NSLog(@"key: %@", key);
if([key isEqualToString:@"opacity"])
{
return (id<CAAction>)[NSNull null];
}
return [super actionForLayer:layer forKey:key];
}
非常に迅速な(しかし明らかにハッキングな)修正が必要な場合は、ただ行う価値があるかもしれません(Swift):
let layer = CALayer()
// set other properties
// ...
layer.speed = 999
CATextLayerの文字列プロパティを変更するときに迷惑な(ぼやけた)アニメーションを無効にするには、次のようにします。
class CANullAction: CAAction {
private static let CA_ANIMATION_CONTENTS = "contents"
@objc
func runActionForKey(event: String, object anObject: AnyObject, arguments dict: [NSObject : AnyObject]?) {
// Do nothing.
}
}
そして、それをそのように使用します(CATextLayerを適切に設定することを忘れないでください、例えば正しいフォントなど):
caTextLayer.actions = [CANullAction.CA_ANIMATION_CONTENTS: CANullAction()]
CATextLayerの完全なセットアップはこちらで確認できます。
private let systemFont16 = UIFont.systemFontOfSize(16.0)
caTextLayer = CATextLayer()
caTextLayer.foregroundColor = UIColor.blackColor().CGColor
caTextLayer.font = CGFontCreateWithFontName(systemFont16.fontName)
caTextLayer.fontSize = systemFont16.pointSize
caTextLayer.alignmentMode = kCAAlignmentCenter
caTextLayer.drawsAsynchronously = false
caTextLayer.actions = [CANullAction.CA_ANIMATION_CONTENTS: CANullAction()]
caTextLayer.contentsScale = UIScreen.mainScreen().scale
caTextLayer.frame = CGRectMake(playbackTimeImage.layer.bounds.Origin.x, ((playbackTimeImage.layer.bounds.height - playbackTimeLayer.fontSize) / 2), playbackTimeImage.layer.bounds.width, playbackTimeLayer.fontSize * 1.2)
uiImageTarget.layer.addSublayer(caTextLayer)
caTextLayer.string = "The text you want to display"
これで、caTextLayer.stringを必要なだけ更新できます=)
これを試して。
let layer = CALayer()
layer.delegate = hoo // Same lifecycle UIView instance.
警告
UITableViewインスタンスのデリゲートを設定すると、クラッシュすることがあります(おそらくscrollviewのヒットテストは再帰的に呼び出されます)。
Swift用に更新され、MacOSではなくiOSで暗黙的なプロパティアニメーションを1つだけ無効にします
// Disable the implicit animation for changes to position
override open class func defaultAction(forKey event: String) -> CAAction? {
if event == #keyPath(position) {
return NSNull()
}
return super.defaultAction(forKey: event)
}
iOS 7現在、これを行う便利なメソッドがあります:
[UIView performWithoutAnimation:^{
// apply changes
}];