2012-12-14 47 views
1

我正在使用父窗口中顯示爲子項的自定義Window對象。
對於這個對象,我想創建一個類似於NSPopover的動畫。創建一個類似於NSPopOver的動畫

我的第一個想法是創建子窗口的屏幕截圖,而不是使用Core Animation爲其動畫並最終顯示真正的窗口。

在討論實現之前,我想知道是否存在更好的方法以及您對我的解決方案的看法。

+1

爲什麼不使用NSPopover? –

+0

因爲我需要一個自定義佈局 – MatterGoal

+0

所以?由於您提供了一個保存其內容視圖的視圖控制器,因此彈出窗口的內容取決於您。 –

回答

3

這不是微不足道的。以下是我的工作方式:

@interface ZoomWindow : NSWindow 
{ 
    CGFloat animationTimeMultiplier; 
} 

@property (nonatomic, readwrite, assign) CGFloat animationTimeMultiplier; 

@end 

@implementation ZoomWindow 

@synthesize animationTimeMultiplier; 

- (NSTimeInterval)animationResizeTime: (NSRect)newWindowFrame 
{ 
    float multiplier = animationTimeMultiplier; 

    if (([[NSApp currentEvent] modifierFlags] & NSShiftKeyMask) != 0) { 
     multiplier *= 10; 
    } 

    return [super animationResizeTime: newWindowFrame] * multiplier; 
} 

@end 

@implementation NSWindow (PecuniaAdditions) 

- (ZoomWindow*)createZoomWindowWithRect: (NSRect)rect 
{ 
    // Code mostly from http://www.noodlesoft.com/blog/2007/06/30/animation-in-the-time-of-tiger-part-1/ 
    // Copyright 2007 Noodlesoft, L.L.C.. All rights reserved. 
    // The code is provided under the MIT license. 

    // The code has been extended to support layer-backed views. However, only the top view is 
    // considered here. The code might not produce the desired output if only a subview has its layer 
    // set. So better set it on the top view (which should cover most cases). 

    NSImageView *imageView; 
    NSImage *image; 
    NSRect frame; 
    BOOL isOneShot; 

    frame = [self frame]; 

    isOneShot = [self isOneShot]; 
    if (isOneShot) { 
     [self setOneShot: NO]; 
    } 

    BOOL hasLayer = [[self contentView] wantsLayer]; 
    if ([self windowNumber] <= 0) // <= 0 if hidden 
    { 
     // We need to temporarily switch off the backing layer of the content view or we get 
     // context errors on the second or following runs of this code. 
     [[self contentView] setWantsLayer: NO]; 

     // Force window device. Kinda crufty but I don't see a visible flash 
     // when doing this. May be a timing thing wrt the vertical refresh. 
     [self orderBack: self]; 
     [self orderOut: self]; 

     [[self contentView] setWantsLayer: hasLayer]; 
    } 

    // Capture the window into an off-screen bitmap. 
    image = [[NSImage alloc] initWithSize: frame.size]; 
    [[self contentView] lockFocus]; 
    NSBitmapImageRep* rep = [[NSBitmapImageRep alloc] initWithFocusedViewRect: NSMakeRect(0.0, 0.0, frame.size.width, frame.size.height)]; 
    [[self contentView] unlockFocus]; 
    [image addRepresentation: rep]; 

    // If the content view is layer-backed the above initWithFocusedViewRect call won't get the content 
    // of the view (seems it doesn't work for CALayers). So we need a second call that captures the 
    // CALayer content and copies it over the captured image (compositing so the window frame and its content). 
    if (hasLayer) 
    { 
     NSRect contentFrame = [[self contentView] bounds]; 
     int bitmapBytesPerRow = 4 * contentFrame.size.width; 

     CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); 
     CGContextRef context = CGBitmapContextCreate (NULL, 
                 contentFrame.size.width, 
                 contentFrame.size.height, 
                 8, 
                 bitmapBytesPerRow, 
                 colorSpace, 
                 kCGImageAlphaPremultipliedLast); 
     CGColorSpaceRelease(colorSpace); 

     [[[self contentView] layer] renderInContext: context]; 
     CGImageRef img = CGBitmapContextCreateImage(context); 
     CFRelease(context); 
     NSImage *subImage = [[NSImage alloc] initWithCGImage: img size: contentFrame.size]; 
     CFRelease(img); 
     [image lockFocus]; 
     [subImage drawAtPoint: NSMakePoint(0, 0) 
        fromRect: NSMakeRect(0, 0, contentFrame.size.width, contentFrame.size.height) 
        operation: NSCompositeCopy 
        fraction: 1]; 
     [image unlockFocus]; 
    } 

    ZoomWindow *zoomWindow = [[ZoomWindow alloc] initWithContentRect: rect 
                  styleMask: NSBorderlessWindowMask 
                  backing: NSBackingStoreBuffered 
                   defer: NO]; 
    zoomWindow.animationTimeMultiplier = 0.3; 
    [zoomWindow setBackgroundColor: [NSColor colorWithDeviceWhite: 0.0 alpha: 0.0]]; 
    [zoomWindow setHasShadow: [self hasShadow]]; 
    [zoomWindow setLevel: [self level]]; 
    [zoomWindow setOpaque: NO]; 
    [zoomWindow setReleasedWhenClosed: NO]; 
    [zoomWindow useOptimizedDrawing: YES]; 

    imageView = [[NSImageView alloc] initWithFrame: [zoomWindow contentRectForFrameRect: frame]]; 
    [imageView setImage: image]; 
    [imageView setImageFrameStyle: NSImageFrameNone]; 
    [imageView setImageScaling: NSScaleToFit]; 
    [imageView setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable]; 

    [zoomWindow setContentView: imageView]; 

    [self setOneShot: isOneShot]; 

    return zoomWindow; 
} 

- (void)fadeIn 
{ 
    [self setAlphaValue: 0.f]; 
    [self orderFront: nil]; 

    [NSAnimationContext beginGrouping]; 
    [[NSAnimationContext currentContext] setDuration: 0.3]; 
    [[self animator] setAlphaValue: 1.f]; 
    [NSAnimationContext endGrouping]; 
} 

- (void)zoomInWithOvershot: (NSRect)overshotFrame withFade: (BOOL)fade makeKey: (BOOL)makeKey 
{ 
    [self setAlphaValue: 0]; 

    NSRect frame = [self frame]; 
    ZoomWindow *zoomWindow = [self createZoomWindowWithRect: frame]; 
    zoomWindow.alphaValue = 0; 

    [zoomWindow orderFront: self]; 

    NSDictionary *windowResize = @{NSViewAnimationTargetKey: zoomWindow, 
            NSViewAnimationEndFrameKey: [NSValue valueWithRect: overshotFrame], 
            NSViewAnimationEffectKey: NSViewAnimationFadeInEffect}; 

    NSArray *animations = @[windowResize]; 
    NSViewAnimation *animation = [[NSViewAnimation alloc] initWithViewAnimations: animations]; 

    [animation setAnimationBlockingMode: NSAnimationBlocking]; 
    [animation setAnimationCurve: NSAnimationEaseIn]; 
    [animation setDuration: 0.2]; 
    [animation startAnimation]; 

    zoomWindow.animationTimeMultiplier = 0.5; 
    [zoomWindow setFrame: frame display: YES animate: YES]; 

    [self setAlphaValue: 1]; 

    if (makeKey) { 
     [self makeKeyAndOrderFront: self]; 
    } else { 
     [self orderFront: self]; 
    } 
    [zoomWindow close]; 
} 

這是在NSWindow類別中實現的。因此,您可以撥打:

- (void)zoomInWithOvershot: (NSRect)overshotFrame withFade: (BOOL)fade makeKey: (BOOL)makeKey 

任何NSWindow。

我應該補充一點,我還沒有能夠同時運行兩個動畫(淡入淡出和大小),但效果與NSPopover的效果非常相似。也許別人可以修復動畫問題。

不用說,在沒有NSPopover的情況下,這個代碼也可以在10.6上運行(這就是爲什麼我先寫它的原因)。

相關問題