29

でマスクを使用するUIViewサブクラスがあります。マスクは独特の形状を使用しており、3 つの角が丸く、残りの角が切り取られた長方形になっています。CAShapeLayerCALayer

UIView標準のアニメーション ブロックを使用してサイズを変更すると、UIViewそれ自体とそのCALayerサイズ変更は問題なく行われます。ただし、マスクは即座に適用されるため、描画の問題が発生します。

を使用してマスクのサイズ変更をアニメーション化しようとしましたCABasicAnimationが、サイズ変更をアニメーション化する運がありませんでした。

どうにかしてマスクのサイズ変更効果をアニメートできますか? マスクを取り除く必要がありますか、それとも現在のマスクの描画方法 ( を使用- (void)drawInContext:(CGContextRef)ctx)を変更する必要がありますか?

乾杯、アレックス

4

9 に答える 9

44

この問題の解決策を見つけました。他の回答は部分的に正しく、役に立ちます。

ソリューションを理解するには、次の点が重要です。

  • mask プロパティ自体はアニメーション化できません。
  • マスクは CALayer であるため、単独でアニメーション化できます。
  • フレームはアニメートできません。境界と位置を使用してください。これはあなたには当てはまらないかもしれませんが(フレームをアニメーション化しようとしていない場合)、私にとっては問題でした. ( Apple QA 1620を参照)
  • ビュー レイヤーのマスクは UIView に関連付けられていないため、ビューのレイヤーに適用されるコア アニメーション トランザクションを受け取りません。
  • CALayer を直接変更しているため、何をしようとしているのかを UIView が把握しているとは期待できません。そのため、UIView アニメーションはコア アニメーション トランザクションを作成してプロパティの変更を含めることはありません。

解決するためには、Core Animation を自分で利用する必要があり、UIView アニメーション ブロックに頼って作業を行うことはできません。

CATransactionで使用しているのと同じ期間の を作成するだけです[UIView animateWithDuration:...]。これにより別のアニメーションが作成されますが、デュレーションとイージング関数が同じであれば、アニメーション ブロック内の他のアニメーションと正確にアニメーション化する必要があります。

NSTimeInterval duration = 0.5;// match this to the value of the UIView animateWithDuration: call

[CATransaction begin];
[CATransaction setValue:[NSNumber numberWithFloat:duration] forKey:kCATransactionAnimationDuration];

self.myView.layer.mask.position = CGPointMake(newX, 0);
self.myView.layer.mask.bounds = CGRectMake(0, 0, newWidth, newHeight);

[CATransaction commit];
于 2012-11-03T18:17:56.810 に答える
13

そのシェイプレイヤーに設定してaCAShapeLayerをマスクするためにaを使用します。UIViewself.layer.mask

ビューのサイズが変更されるたびにマスクをアニメーション化するには-setBounds:、アニメーション中に境界が変更された場合にマスク レイヤー パスをアニメーション化するように上書きしました。

実装方法は次のとおりです。

- (void)setBounds:(CGRect)bounds
{
    [super setBounds:bounds];
    CAPropertyAnimation *boundsAnimation = (CABasicAnimation *)[self.layer animationForKey:@"bounds"];

    // update the mask
    self.maskLayer.frame = self.layer.bounds;

    // if the bounds change happens within an animation, also animate the mask path
    if (!boundsAnimation) {
        self.maskLayer.path = [self createMaskPath];
    } else {
        // copying the original animation allows us to keep all animation settings
        CABasicAnimation *animation = [boundsAnimation copy];
        animation.keyPath = @"path";

        CGPathRef newPath = [self createMaskPath];
        animation.fromValue = (id)self.maskLayer.path;
        animation.toValue = (__bridge id)newPath;

        self.maskLayer.path = newPath;

        [self.maskLayer addAnimation:animation forKey:@"path"];
    }
}

(例self.maskLayerでは`self.layer.maskに設定されています)

My-createMaskPathは、ビューをマスクするために使用する CGPathRef を計算します。のマスク パスも更新し-layoutSubviewsます。

于 2014-03-20T18:05:08.897 に答える
5

CALayerのマスクプロパティはアニメートできません。これは、その方向での運の欠如を説明しています。

マスクの描画は、マスクのフレーム/境界に依存しますか?(コードを提供できますか?)マスクにはneedsDisplayOnBoundsChangeプロパティが設定されていますか?

乾杯、コリン

于 2010-06-01T11:29:41.623 に答える
2

マスクパラメータはアニメーションしませんが、マスクとして設定されているレイヤーをアニメーション化できます...

CAShapeLayer の Path プロパティをアニメーション化すると、マスクがアニメーション化されます。これが自分のプロジェクトから機能することを確認できます。ただし、ベクトル以外のマスクの使用についてはわかりません。マスクのコンテンツ プロパティをアニメーション化してみましたか?

ありがとう、ジョン

于 2010-09-28T12:23:02.940 に答える
0

マスクの変更をアニメーション化することができます。

マスク レイヤーとして CAShapeLayer を使用することを好みます。プロパティ パスを使用してマスクの変更をアニメーション化すると非常に便利です。

変更をアニメーション化する前に、ソースのコンテンツをインスタンス CGImageRef にダンプし、アニメーション用の新しいレイヤーを作成します。アニメーション中は元のレイヤーを非表示にし、アニメーションの終了時に表示します。

プロパティ パスにキー アニメーションを作成するサンプル コードを次に示します。独自のパス アニメーションを作成する場合は、パスに常に同じ数のポイントがあることを確認してください。

- (CALayer*)_mosaicMergeLayer:(CGRect)bounds content:(CGImageRef)content isUp:(BOOL)isUp {

    CALayer* layer = [CALayer layer];
    layer.frame = bounds;
    layer.backgroundColor = [[UIColor clearColor] CGColor];
    layer.contents = (id)content;

    CAShapeLayer* maskLayer = [CAShapeLayer layer];
    maskLayer.fillColor = [[UIColor blackColor] CGColor];
    maskLayer.frame = bounds;
    maskLayer.fillRule = kCAFillRuleEvenOdd;
    maskLayer.path = ( isUp ? [self _maskArrowUp:-bounds.size.height*2] : [self _maskArrowDown:bounds.size.height*2] );
    layer.mask = maskLayer;

    CAKeyframeAnimation* ani = [CAKeyframeAnimation animationWithKeyPath:@"path"];
    ani.removedOnCompletion = YES;
    ani.duration = 0.3f;
    ani.fillMode = kCAFillModeForwards;
    ani.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];

    NSArray* values = ( isUp ?
        [NSArray arrayWithObjects:
        (id)[self _maskArrowUp:0],
        (id)[self _maskArrowUp:-ceilf(bounds.size.height*1.2)],
        nil] 
    :       
        [NSArray arrayWithObjects:
        (id)[self _maskArrowDown:0],
        (id)[self _maskArrowDown:bounds.size.height],
        nil] 
    );
    ani.values = values;
    ani.delegate = self;
    [maskLayer addAnimation:ani forKey:nil];

    return layer;
}

- (void)_startMosaicMergeAni:(BOOL)up {

    CALayer* overlayer = self.aniLayer;
    CGRect bounds = overlayer.bounds;
    self.firstHalfAni = NO;

    CALayer* frontLayer = nil;
    frontLayer = [self _mosaicMergeLayer:bounds 
                                content:self.toViewSnapshot 
                                isUp:up];
    overlayer.contents = (id)self.fromViewSnapshot;
    [overlayer addSublayer:frontLayer];
}
于 2013-01-03T12:13:48.703 に答える
0

プログラムによる解決策が見つからなかったため、正しい形状とアルファ値で png 画像を描画し、代わりにそれを使用しました。これでマスクいらない…

于 2012-06-21T07:48:31.267 に答える