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

浙公網安備 33010602011771號