141

-drawInContext:メソッドに複雑な描画コードを含むレイヤーがあります。必要な描画の量を最小限に抑えようとしているので、-setNeedsDisplayInRect:を使用して、変更された部分だけを更新しています。これは見事に機能しています。ただし、グラフィックシステムがレイヤーを更新すると、クロスフェードを使用して古い画像から新しい画像に移行します。すぐに切り替えて欲しいのですが。

CATransactionを使用してアクションをオフにし、期間をゼロに設定しようとしましたが、どちらも機能しません。これが私が使用しているコードです:

[CATransaction begin];
[CATransaction setDisableActions: YES];
[self setNeedsDisplayInRect: rect];
[CATransaction commit];

代わりに使用する必要があるCATransactionに別のメソッドがありますか(-setValue:forKey:をkCATransactionDisableActionsで試しましたが、同じ結果です)。

4

15 に答える 15

178

これを行うには、レイヤーのアクションディクショナリを設定して[NSNull null]、適切なキーのアニメーションとして返すようにします。たとえば、私は

NSDictionary *newActions = @{
    @"onOrderIn": [NSNull null],
    @"onOrderOut": [NSNull null],
    @"sublayers": [NSNull null],
    @"contents": [NSNull null],
    @"bounds": [NSNull null]
};

layer.actions = newActions;

レイヤーの1つ内のサブレイヤーの挿入または変更、およびレイヤーのサイズとコンテンツの変更時に、フェードイン/フェードアウトアニメーションを無効にします。更新された図面でのクロスフェードを防ぐために、contentsキーはあなたが探しているものだと思います。


Swiftバージョン:

let newActions = [
        "onOrderIn": NSNull(),
        "onOrderOut": NSNull(),
        "sublayers": NSNull(),
        "contents": NSNull(),
        "bounds": NSNull(),
    ]
于 2010-02-11T13:37:14.977 に答える
90

また:

[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];

//foo

[CATransaction commit];
于 2011-03-30T16:11:20.853 に答える
32

レイヤーのプロパティを変更すると、CAは通常、変更をアニメーション化するための暗黙的なトランザクションオブジェクトを作成します。変更をアニメーション化したくない場合は、明示的なトランザクションを作成し、そのkCATransactionDisableActionsプロパティをtrueに設定することで、暗黙的なアニメーションを無効にすることができます。

Objective-C

[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
// change properties here without animation
[CATransaction commit];

迅速

CATransaction.begin()
CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions)
// change properties here without animation
CATransaction.commit()
于 2014-07-08T10:05:32.913 に答える
23

Brad Larsonの答えに加えて、(自分で作成した)カスタムレイヤーの場合、レイヤーのディクショナリを変更する代わりに委任を使用できます。actionsこのアプローチはより動的であり、よりパフォーマンスが高い可能性があります。また、アニメーション化可能なすべてのキーを一覧表示しなくても、すべての暗黙的なアニメーションを無効にすることができます。

残念ながら、UIViewsをカスタムレイヤーデリゲートとして使用することはできません。これは、それぞれUIViewがすでに独自のレイヤーのデリゲートであるためです。ただし、次のような単純なヘルパークラスを使用できます。

@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説明されているように、適切なアプローチはカスタムプロパティを使用することです。

于 2013-04-19T02:32:14.293 に答える
9

これは、受け入れられた回答に似ていますが、Swift用のより効率的なソリューションです。場合によっては、値を変更するたびにトランザクションを作成するよりも優れていることがあります。これは、他の人が言及しているように、パフォーマンスの問題です。たとえば、レイヤー位置を60fpsでドラッグする一般的な使用例です。

// Disable implicit position animation.
layer.actions = ["position": NSNull()]      

レイヤーアクションがどのように解決されるかについては、アップルのドキュメントを参照してください。デリゲートを実装すると、カスケード内のもう1つのレベルがスキップされますが、私の場合、デリゲートを関連付けられたUIViewに設定する必要があるという警告のため、これは面倒でした。

NSNull編集:に準拠していることを指摘しているコメント投稿者のおかげで更新されましたCAAction

于 2015-10-29T23:02:08.603 に答える
8

実際、私は正しい答えを見つけることができませんでした。私にとって問題を解決する方法はこれでした:

- (id<CAAction>)actionForKey:(NSString *)event {   
    return nil;   
}

次に、特定のアニメーションを無効にするために、その中の任意のロジックを実行できますが、それらをすべて削除したかったので、nilを返しました。

于 2013-05-21T10:48:40.237 に答える
7

サムの答えとサイモンの難しさに基づいて...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];

}
于 2013-12-03T21:07:33.333 に答える
6

キーをCATransaction内部的に呼び出す内部のアクションを無効にするためのより簡単な方法を見つけました。setValue:forKey:kCATransactionDisableActions

[CATransaction setDisableActions:YES];

迅速:

CATransaction.setDisableActions(true)
于 2015-03-24T11:10:27.183 に答える
6

Swiftで暗黙のレイヤーアニメーションを無効にするには

CATransaction.setDisableActions(true)
于 2018-04-10T17:08:39.770 に答える
4

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)
}

別の例、この場合、2つの暗黙のアニメーションを削除します。

class RepairedGradientLayer: CAGradientLayer {

    // Totally ELIMINATE idiotic implicit animations, in this example when
    // we hide or move the gradient layer

    override open class func defaultAction(forKey event: String) -> CAAction? {
        if event == #keyPath(position) {
            return NSNull()
        }
        if event == #keyPath(isHidden) {
            return NSNull()
        }
        return super.defaultAction(forKey: event)
    }
}
于 2019-07-11T00:54:39.700 に答える
2

これを、-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];
}
于 2015-01-14T11:53:01.963 に答える
1

非常に迅速な(しかし確かにハッキーな)修正が必要な場合は、(Swift)を実行するだけの価値があるかもしれません。

let layer = CALayer()

// set other properties
// ...

layer.speed = 999
于 2016-05-28T09:12:32.870 に答える
0

iOS 7の時点で、これを行う便利な方法があります。

[UIView performWithoutAnimation:^{
    // apply changes
}];
于 2015-08-01T18:39:51.130 に答える
0

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を必要なだけ更新できます=)

これこの答えに触発されました。

于 2016-03-11T20:28:58.453 に答える
0

これを試して。

let layer = CALayer()
layer.delegate = hoo // Same lifecycle UIView instance.

警告

UITableViewインスタンスのデリゲートを設定すると、クラッシュが発生することがあります(おそらく、scrollviewのヒットテストが再帰的に呼び出されます)。

于 2016-04-06T17:04:32.613 に答える