我正在尝试创建一个复制Dashboard小部件行为的Card类,因为您可以将控件或图像或其他任何内容放在卡的两侧并在它们之间切换.
图层支持的视图具有转换属性,但更改它不会执行我期望它做的操作(围绕y轴旋转图层将其折叠到左侧).
我被指向了一些未记录的功能和一个名为cgsprivate.h的.h文件,但我想知道是否有正式的方法来执行此操作?这个软件必须发货,我讨厌看到它失败,因为苹果公司的人在10.6中取得了它.
任何人都知道如何做到这一点?对于我来说,如此简单的小部件在Core Animation中很难做到.
提前致谢!
编辑:我可以使用图层上的图像来完成此行为,但我不知道如何在图层上获得更高级的控件/视图/任何内容.卡片示例使用图像.
Mike Lee有一个翻转效果的实现,他已经发布了一些示例代码.(不幸的是,这已不再在网上提供了,但Drew McCormack 在他自己的实现中建立了这个.)看来他抓住了要交换的"背景"和"前景"视图的图层,使用CATransform3D来旋转动画中的两个视图,然后在动画完成后交换视图.
通过使用视图中的图层,您可以避免需要缓存到位图中,因为这是图层正在执行的操作.无论如何,他的视图控制器看起来是你想要的一个很好的插入式解决方案.
使用像e.James概述的核心动画...注意,这是使用垃圾收集和托管层:
#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
编辑:这将动画从一个窗口翻转到另一个窗口.您可以将相同的主体应用于视图.