9

コントロールや画像などをカードの両面に配置して、それらの間をめくることができるという点で、ダッシュボードウィジェットの動作を複製する Card クラスを作成しようとしています。

レイヤーに裏打ちされたビューには変換プロパティがありますが、それを変更すると、期待どおりの結果が得られません (レイヤーを y 軸を中心に回転させると、レイヤーが左側に折りたたまれます)。

いくつかの文書化されていない機能と cgsprivate.h という名前の .h ファイルを指摘されましたが、これを行う公式の方法があるかどうか疑問に思っています。このソフトウェアは出荷する必要があり、Apple の担当者が 10.6 でプルしたため、後で失敗するのは見たくありません。

誰でもこれを行う方法を知っていますか? Core Animation で単純なウィジェットを実行するのが非常に難しいというのは、私にはとても奇妙です。

前もって感謝します!

編集:レイヤー上にある画像でこの動作を実現できますが、レイヤー上でより高度なコントロール/ビュー/その他のものを取得する方法がわかりません。カードの例では画像を使用しています。

4

7 に答える 7

11

Mike Leeは、いくつかのサンプルコードをリリースしたフリップエフェクトの実装を持っています。(残念ながら、これはオンラインでは利用できなくなりましたが、Drew McCormackは、彼自身の実装でそれを基に構築しました。)スワップする「背景」ビューと「前景」ビューのレイヤーを取得し、CATransform3Dを使用して回転させるようです。アニメーション内の2つのビュー。アニメーションが完了すると、ビューが入れ替わります。

ビューのレイヤーを使用することで、ビットマップにキャッシュする必要がなくなります。これは、レイヤーがとにかく行っていることだからです。いずれにせよ、彼のビューコントローラーはあなたが望むもののための良いドロップインソリューションであるように見えます。

于 2008-12-16T19:37:49.590 に答える
6

e.James が概説したような Core Animation の使用...注、これはガベージ コレクションとホスト レイヤーを使用しています。

#import "AnimationWindows.h"

@interface AnimationFlipWindow (PrivateMethods)

NSRect RectToScreen(NSRect aRect, NSView *aView);
NSRect RectFromScreen(NSRect aRect, NSView *aView);
NSRect RectFromViewToView(NSRect aRect, NSView *fromView, NSView *toView);

@end

#pragma mark -

@implementation AnimationFlipWindow

@synthesize flipForward = _flipForward;

- (id) init {

    if ( self = [super init] ) { 
        _flipForward = YES; 
    }

    return self;
}

- (void) finalize {

    // Hint to GC for cleanup
    [[NSGarbageCollector defaultCollector] collectIfNeeded];
    [super finalize];
}

- (void) flip:(NSWindow *)activeWindow 
       toBack:(NSWindow *)targetWindow {

    CGFloat duration = 1.0f * (activeWindow.currentEvent.modifierFlags & NSShiftKeyMask ? 10.0 : 1.0);
    CGFloat zDistance = 1500.0f;

    NSView *activeView = [activeWindow.contentView superview];
    NSView *targetView = [targetWindow.contentView superview];

    // Create an animation window
    CGFloat maxWidth  = MAX(NSWidth(activeWindow.frame), NSWidth(targetWindow.frame)) + 500;
    CGFloat maxHeight = MAX(NSHeight(activeWindow.frame), NSHeight(targetWindow.frame)) + 500;

    CGRect animationFrame = CGRectMake(NSMidX(activeWindow.frame) - (maxWidth / 2), 
                                       NSMidY(activeWindow.frame) - (maxHeight / 2), 
                                       maxWidth, 
                                       maxHeight);

    mAnimationWindow = [NSWindow initForAnimation:NSRectFromCGRect(animationFrame)];

    // Add a touch of perspective
    CATransform3D transform = CATransform3DIdentity; 
    transform.m34 = -1.0 / zDistance;
    [mAnimationWindow.contentView layer].sublayerTransform = transform;

    // Relocate target window near active window
    CGRect targetFrame = CGRectMake(NSMidX(activeWindow.frame) - (NSWidth(targetWindow.frame) / 2 ), 
                                    NSMaxY(activeWindow.frame) - NSHeight(targetWindow.frame),
                                    NSWidth(targetWindow.frame),
                                    NSHeight(targetWindow.frame));

    [targetWindow setFrame:NSRectFromCGRect(targetFrame) display:NO];

    mTargetWindow = targetWindow;

    // New Active/Target Layers
    [CATransaction begin];
    CALayer *activeWindowLayer = [activeView layerFromWindow];
    CALayer *targetWindowLayer = [targetView layerFromWindow];
    [CATransaction commit];

    activeWindowLayer.frame = NSRectToCGRect(RectFromViewToView(activeView.frame, activeView, [mAnimationWindow contentView]));
    targetWindowLayer.frame = NSRectToCGRect(RectFromViewToView(targetView.frame, targetView, [mAnimationWindow contentView]));

    [CATransaction begin];
    [[mAnimationWindow.contentView layer] addSublayer:activeWindowLayer];
    [CATransaction commit];

    [mAnimationWindow orderFront:nil];  

    [CATransaction begin];
    [[mAnimationWindow.contentView layer] addSublayer:targetWindowLayer];
    [CATransaction commit];

    // Animate our new layers
    [CATransaction begin];
    CAAnimation *activeAnim = [CAAnimation animationWithDuration:(duration * 0.5) flip:YES forward:_flipForward];
    CAAnimation *targetAnim = [CAAnimation animationWithDuration:(duration * 0.5) flip:NO  forward:_flipForward];
    [CATransaction commit];

    targetAnim.delegate = self;
    [activeWindow orderOut:nil];

    [CATransaction begin];
    [activeWindowLayer addAnimation:activeAnim forKey:@"flip"];
    [targetWindowLayer addAnimation:targetAnim forKey:@"flip"];
    [CATransaction commit];
}

- (void) animationDidStop:(CAAnimation *)animation finished:(BOOL)flag {

    if (flag) {
        [mTargetWindow makeKeyAndOrderFront:nil];
        [mAnimationWindow orderOut:nil];

        mTargetWindow = nil;
        mAnimationWindow = nil;
    }
}

#pragma mark PrivateMethods:

NSRect RectToScreen(NSRect aRect, NSView *aView) {
    aRect = [aView convertRect:aRect toView:nil];
    aRect.origin = [aView.window convertBaseToScreen:aRect.origin];
    return aRect;
}

NSRect RectFromScreen(NSRect aRect, NSView *aView) {
    aRect.origin = [aView.window convertScreenToBase:aRect.origin];
    aRect = [aView convertRect:aRect fromView:nil];
    return aRect;
}

NSRect RectFromViewToView(NSRect aRect, NSView *fromView, NSView *toView) {

    aRect = RectToScreen(aRect, fromView);
    aRect = RectFromScreen(aRect, toView);

    return aRect;
}

@end

#pragma mark -
#pragma mark CategoryMethods:

@implementation CAAnimation (AnimationFlipWindow)

+ (CAAnimation *) animationWithDuration:(CGFloat)time flip:(BOOL)bFlip forward:(BOOL)forwardFlip{

    CABasicAnimation *flipAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];

    CGFloat startValue, endValue;

    if ( forwardFlip ) {
        startValue = bFlip ? 0.0f : -M_PI;
        endValue = bFlip ? M_PI : 0.0f;
    } else {
        startValue = bFlip ? 0.0f : M_PI;
        endValue = bFlip ? -M_PI : 0.0f;
    }

    flipAnimation.fromValue = [NSNumber numberWithDouble:startValue];
    flipAnimation.toValue = [NSNumber numberWithDouble:endValue];

    CABasicAnimation *shrinkAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
    shrinkAnimation.toValue = [NSNumber numberWithFloat:1.3f];
    shrinkAnimation.duration = time * 0.5;
    shrinkAnimation.autoreverses = YES;

    CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
    animationGroup.animations = [NSArray arrayWithObjects:flipAnimation, shrinkAnimation, nil];
    animationGroup.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    animationGroup.duration = time;
    animationGroup.fillMode = kCAFillModeForwards;
    animationGroup.removedOnCompletion = NO;

    return animationGroup;
}

@end

#pragma mark -

@implementation NSWindow (AnimationFlipWindow)

+ (NSWindow *) initForAnimation:(NSRect)aFrame {

    NSWindow *window =  [[NSWindow alloc] initWithContentRect:aFrame 
                                                    styleMask:NSBorderlessWindowMask 
                                                      backing:NSBackingStoreBuffered 
                                                        defer:NO];
    [window setOpaque:NO];
    [window setHasShadow:NO];
    [window setBackgroundColor:[NSColor clearColor]];
    [window.contentView setWantsLayer:YES];

    return window;
}

@end

#pragma mark -

@implementation NSView (AnimationFlipWindow)

- (CALayer *) layerFromWindow {

    NSBitmapImageRep *image = [self bitmapImageRepForCachingDisplayInRect:self.bounds];
    [self cacheDisplayInRect:self.bounds toBitmapImageRep:image];

    CALayer *layer = [CALayer layer];
    layer.contents = (id)image.CGImage;
    layer.doubleSided = NO;

    // Shadow settings based upon Mac OS X 10.6
    [layer setShadowOpacity:0.5f];
    [layer setShadowOffset:CGSizeMake(0,-10)];
    [layer setShadowRadius:15.0f];


    return layer;
}

@end

ヘッダー ファイル:

@interface AnimationFlipWindow : NSObject {

    BOOL _flipForward;

    NSWindow *mAnimationWindow;
    NSWindow *mTargetWindow;
}

// Direction of flip animation (property)
@property (readwrite, getter=isFlipForward) BOOL flipForward;

- (void) flip:(NSWindow *)activeWindow 
       toBack:(NSWindow *)targetWindow;
@end

#pragma mark -
#pragma mark CategoryMethods:

@interface CAAnimation (AnimationFlipWindow)
+ (CAAnimation *) animationWithDuration:(CGFloat)time 
                                   flip:(BOOL)bFlip // Flip for each side
                                forward:(BOOL)forwardFlip; // Direction of flip
@end

@interface NSWindow (AnimationFlipWindow)
+ (NSWindow *) initForAnimation:(NSRect)aFrame;
@end

@interface NSView (AnimationFlipWindow)
- (CALayer *) layerFromWindow;
@end

編集:これは、あるウィンドウから別のウィンドウにフリップするようにアニメーション化されます。同じプリンシパルをビューに適用できます。

于 2011-02-22T19:35:06.543 に答える
3

あなたの目的にはやり過ぎですが (大部分が完成したボードおよびカード ゲームのリファレンス アプリが含まれているため)、ADC からのこのサンプルを確認してください。それに含まれているカードゲームは、その反転効果を非常にうまく行います.

于 2008-12-16T17:31:56.867 に答える
2

画像でこれを行うことができる場合は、おそらくすべてのコントロールをNSViewオブジェクトに保持し (通常どおり)、反転効果を実行する直前に をNSView使用してビットマップ画像にレンダリングできます。cacheDisplayInRect:toBitmapImageRep:手順は次のとおりです。

  1. NSViewをビットマップに レンダリングする
  2. そのビットマップを反転効果に適したレイヤーに表示します
  3. イメージ レイヤを非表示にしNSViewて公開する
  4. フリップ効果を実行する
于 2008-12-16T18:58:07.473 に答える
1

私はこれが遅れていることを知っていますが、Apple はここにサンプルプロジェクトを持っています。

https://developer.apple.com/library/mac/#samplecode/ImageTransition/Introduction/Intro.html#//apple_ref/doc/uid/DTS40010277

于 2013-04-03T12:21:52.747 に答える
0

Mizage の担当者による完全なオープン ソース実装があります。

ここで確認できます: https://github.com/mizage/Flip-Animation

于 2011-10-27T15:48:47.317 に答える
-3

この質問がされた 2008 年にはおそらくそうではありませんでしたが、最近では非常に簡単です。

[UIView animateWithDuration:0.5 animations:^{
    [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:self.iconView cache:YES];
    /* changes to the view made here will be reflected on the flipped to side */
}];

注: どうやら、これは iOS でのみ機能します。

于 2015-01-30T22:42:34.363 に答える