gpt4 book ai didi

cocoa - 创建类似 NSPopOver 的动画

转载 作者:行者123 更新时间:2023-12-03 16:58:10 24 4
gpt4 key购买 nike

我正在处理一个自定义 Window 对象,该对象在父 Window 中显示为子窗口。
对于这个对象,我想创建一个类似于 NSPopover 的动画。

我的第一个想法是创建子窗口的屏幕截图,然后使用核心动画对其进行动画处理,最后显示真实的窗口。

在请求实现之前,我想知道是否存在更好的方法以及您对我的解决方案的看法。

最佳答案

这不是小事。我是这样做的:

@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(这就是我首先编写它的原因)。

关于cocoa - 创建类似 NSPopOver 的动画,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13885715/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com