Mac开发基础08-NSWindow(二)

NSWindow 其他使用和技巧

NSWindow 是 macOS 应用程序中用于显示和管理窗口的核心类。可用于创建、编辑和管理应用程序的窗口。

1. 自定义窗口的内容视图层级

替换默认的内容视图

NSWindow 默认包含一个内容视图,你可以使用自定义内容视图来替换它。

Objective-C
NSView *customView = [[NSView alloc] initWithFrame:window.contentView.bounds];
window.contentView = customView;
Swift
let customView = NSView(frame: window.contentView!.bounds)
window.contentView = customView

使用 NSViewController 管理内容视图

你可以使用 NSViewController 来管理窗口的内容视图,这样可以更好地组织代码和管理视图逻辑。

Objective-C
NSViewController *customViewController = [[NSViewController alloc] init];
customViewController.view = customView;
window.contentViewController = customViewController;
Swift
let customViewController = NSViewController()
customViewController.view = customView
window.contentViewController = customViewController

2. 实现自定义窗口动画效果

使用 Core Animation 自定义动画

为了使窗口的出现更具吸引力,可以使用 Core Animation 为其添加淡入淡出的动画效果。

Objective-C
#import <QuartzCore/QuartzCore.h>

- (void)animateWindowAppearance:(NSWindow *)window {
    CABasicAnimation *fadeAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
    fadeAnimation.fromValue = @0;
    fadeAnimation.toValue = @1;
    fadeAnimation.duration = 1.0;

    [window.contentView setWantsLayer:YES];
    [window.contentView.layer addAnimation:fadeAnimation forKey:@"fadeIn"];
    window.alphaValue = 1;
}
Swift
import QuartzCore

func animateWindowAppearance(_ window: NSWindow) {
    let fadeAnimation = CABasicAnimation(keyPath: "opacity")
    fadeAnimation.fromValue = 0
    fadeAnimation.toValue = 1
    fadeAnimation.duration = 1.0
    
    window.contentView?.wantsLayer = true
    window.contentView?.layer?.add(fadeAnimation, forKey: "fadeIn")
    window.alphaValue = 1
}

3. 窗口模态(Modal)展示

模态窗口用于阻止用户与其他窗口交互直到完成当前窗口的任务。可以应用模态到整个应用或某个特定窗口。

应用模态

Objective-C
NSWindow *modalWindow = [[NSWindow alloc] initWithContentRect:frame
                                                    styleMask:NSWindowStyleMaskTitled
                                                      backing:NSBackingStoreBuffered
                                                        defer:NO];
[NSApp runModalForWindow:modalWindow];
Swift
let modalWindow = NSWindow(contentRect: frame, styleMask: [.titled], backing: .buffered, defer: false)
NSApp.runModal(for: modalWindow)

窗口模态

Objective-C
[window beginSheet:modalWindow completionHandler:^(NSModalResponse returnCode) {
    // 处理模态结束
}];
Swift
window.beginSheet(modalWindow, completionHandler: { response in
    // 处理模态结束
})

4. 自定义窗口阴影

阴影可以使窗口看起来更立体,以下为自定义窗口阴影的示例。

Objective-C
NSShadow *shadow = [[NSShadow alloc] init];
shadow.shadowBlurRadius = 10.0;
shadow.shadowOffset = NSMakeSize(5, -5);
shadow.shadowColor = [[NSColor blackColor] colorWithAlphaComponent:0.5];
window.shadow = shadow;
Swift
let shadow = NSShadow()
shadow.shadowBlurRadius = 10.0
shadow.shadowOffset = NSSize(width: 5, height: -5)
shadow.shadowColor = NSColor.black.withAlphaComponent(0.5)
window.shadow = shadow

5. 验证窗口大小调整

可以通过实现 windowWillResize 方法来控制窗口调整大小的范围。

Objective-C
- (NSSize)windowWillResize:(NSWindow *)sender toSize:(NSSize)frameSize {
    NSSize newSize = frameSize;
    if (frameSize.width < 800) {
        newSize.width = 800;
    }
    if (frameSize.height < 600) {
        newSize.height = 600;
    }
    return newSize;
}
Swift
func windowWillResize(_ sender: NSWindow, to frameSize: NSSize) -> NSSize {
    var newSize = frameSize
    if frameSize.width < 800 {
        newSize.width = 800
    }
    if frameSize.height < 600 {
        newSize.height = 600
    }
    return newSize
}

6. 自定义窗口关闭行为

在关闭窗口前,可以弹出警告框来确认用户的操作。

Objective-C
- (BOOL)windowShouldClose:(NSWindow *)sender {
    NSAlert *alert = [[NSAlert alloc] init];
    [alert setMessageText:@"Confirm"];
    [alert setInformativeText:@"Are you sure you want to close this window?"];
    [alert addButtonWithTitle:@"Yes"];
    [alert addButtonWithTitle:@"No"];
    [alert setAlertStyle:NSAlertStyleWarning];
    return ([alert runModal] == NSAlertFirstButtonReturn);
}
Swift
func windowShouldClose(_ sender: NSWindow) -> Bool {
    let alert = NSAlert()
    alert.messageText = "Confirm"
    alert.informativeText = "Are you sure you want to close this window?"
    alert.alertStyle = .warning
    alert.addButton(withTitle: "Yes")
    alert.addButton(withTitle: "No")
    return alert.runModal() == .alertFirstButtonReturn
}

7. 窗口透明度和鼠标事件传递

设置窗口透明度

窗口透明度和背景透明设置可以实现具有不规则形状和透明背景的窗口。

Objective-C
window.opaque = NO;
window.backgroundColor = [NSColor clearColor];
Swift
window.isOpaque = false
window.backgroundColor = .clear

忽略鼠标事件

如需让窗口忽略鼠标事件,可以设置 ignoresMouseEvents 属性:

Objective-C
window.ignoresMouseEvents = YES;
Swift
window.ignoresMouseEvents = true

8. 使用 NSWindowController 管理窗口

使用 NSWindowController 可以更好地管理和组织你的窗口代码,比如在多窗口应用程序中。

Objective-C
NSWindowController *windowController = [[NSWindowController alloc] initWithWindow:window];
[windowController showWindow:nil];
Swift
let windowController = NSWindowController(window: window)
windowController.showWindow(nil)

9. 窗口层级管理

管理窗口层级可以控制窗口在不同层级的显示顺序。例如,将窗口设置为浮动窗口使它总是在其他窗口之上。

Objective-C
window.level = NSFloatingWindowLevel;
Swift
window.level = .floating

10. 使用 KVO 观察属性变化

通过 KVO 机制,可以监听窗口的属性变化,并根据变化作出相应处理。

Objective-C
@interface CustomWindowObserver : NSObject

@property (nonatomic, strong) NSWindow *observedWindow;

- (instancetype)initWithWindow:(NSWindow *)window;

@end

@implementation CustomWindowObserver

- (instancetype)initWithWindow:(NSWindow *)window {
    if (self = [super init]) {
        self.observedWindow = window;
        [window addObserver:self forKeyPath:@"isVisible" options:NSKeyValueObservingOptionNew context:nil];
    }
    return self;
}

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary<NSKeyValueChangeKey,id> *)change
                       context:(void *)context {
    if ([keyPath isEqualToString:@"isVisible"]) {
        NSLog(@"Window visibility changed: %@", change[NSKeyValueChangeNewKey]);
    }
}

- (void)dealloc {
    [self.observedWindow removeObserver:self forKeyPath:@"isVisible"];
}

@end
Swift
class CustomWindowObserver: NSObject {
    private var observation: NSKeyValueObservation?

    init(window: NSWindow) {
        super.init()
        observation = window.observe(\.isVisible, options: [.new]) { window, change in
            if let isVisible = change.newValue {
                print("Window visibility changed: \(isVisible)")
            }
        }
    }
}

底层实现与原理

1. 窗口管理由 AppKit 和 Core Graphics 协同完成

NSWindow 是 AppKit 框架的一部分,但其底层渲染和管理依赖于 Core Graphics 框架。AppKit 负责窗口的高层逻辑和事件处理,而 Core Graphics 负责实际的渲染与显示。

2. 窗口的层级管理

在 macOS 中,窗口有不同的层级,如普通窗口、浮动窗口和主窗口。不同层级的窗口管理由系统层面的 WindowServer 处理。通过设置窗口的 level 属性,开发者可以控制窗口在这些层级之间切换。

3. 窗口的事件处理机制

窗口的事件处理包括鼠标、键盘等用户交互事件。这些事件会优先传递到对应的 NSWindow,再由窗口进一步传递到其内容视图或子视图。AppKit 通过事件循环(Run Loop)来管理事件的分发与处理。

4. 窗口的图像缓存

为了提升性能,窗口的内容通常会被缓存起来。如果窗口内容频繁变化,可以通过设置 backingType 属性来优化绘制性能。

Objective-C
window.backingType = NSBackingStoreBuffered;
Swift
window.backingType = .buffered
posted @ 2024-08-06 14:34  Mr.陳  阅读(75)  评论(0编辑  收藏  举报