由于項目原來的設計,導致在OS X 10.10之后的系統上會出現一些奇怪的Bug,調用
- addSubview:positioned:relativeTo:
方法卻并沒有實現將視圖放在最上層。經過不斷的查找,才知道,子視圖的父視圖并沒有被顯示出來,導致了- addSubview:positioned:relativeTo:
方法不起作用。那么如果將父視圖顯示出來的話,window button(關閉按鈕,最小化按鈕,全屏按鈕,最大化窗口按鈕)又會被遮蓋掉,無奈,需要自定義window button。
在自定義之前一定要將系統自動創建的按鈕關閉
[[self.window standardWindowButton:NSWindowCloseButton] setEnabled:NO];
[[self.window standardWindowButton:NSWindowMiniaturizeButton] setEnabled:NO];
[[self.window standardWindowButton:NSWindowFullScreenButton] setEnabled:NO];
[[self.window standardWindowButton:NSWindowZoomButton] setEnabled:NO];
自定義左上角的三個控制按鈕
這里我是將三個按鈕寫到了一個自定義View中,方便布局用。
首先是頭文件中聲明:
#import <Cocoa/Cocoa.h>
@interface CustomWindowButtonView : NSView {
@private
BOOL mouseInside_;
NSButton *closeButton_;
NSButton *minitButton_;
NSButton *zoomButton_;
}
@property (nonatomic, assign) BOOL mouseInside;
@property (nonatomic, retain) NSButton *closeButton;
@property (nonatomic, retain) NSButton *minitButton;
@property (nonatomic, retain) NSButton *zoomButton;
@end
mouseInside參數是用于判斷鼠標是否在視圖之中。
創建一個window button所用到的方法是+ standardWindowButton:forStyleMask:
文檔中相關說明:
Returns a new instance of a given standard window button, sized appropriately for a given window style.
這里說明了,此方法是返回一個標準的window button實例,并且會設置合適的window style。
NSWindowButton
取值如下:
enum {
NSWindowCloseButton,
NSWindowMiniaturizeButton,
NSWindowZoomButton,
NSWindowToolbarButton,
NSWindowDocumentIconButton,
NSWindowDocumentVersionsButton = 6,
NSWindowFullScreenButton,
};
typedef NSUInteger NSWindowButton;
名字很清楚的顯示了window button的作用,這里我們需要自定義的是左邊的三個按鈕,所以需要用到的是NSWindowCloseButton
(關閉窗口按鈕),NSWindowMiniaturizeButton
(最小化窗口按鈕)和NSWindowZoomButton
(最大化窗口按鈕,不是全屏按鈕)。
注意
在OS X 10.10之后的版本中,NSWindowZoomButton
會變成全屏按鈕,而不是10.10之前的最大化窗口按鈕。
第二個參數設置成self.window.styleMask
就OK了。
這里windows button需要實現系統的懸停,響應窗口和非響應窗口的不同效果,那么就要實現鼠標移入和移出的方法- mouseEntered:
和- mouseExited:
,但是實現這個方法,需要重寫- updateTrackingAreas
方法,因為當大小或坐標改變后就會造成所指定的檢測區域錯誤,所以我們需要重寫updateTrackingAreas
方法,將創建NSTrackingArea
的工作放在其中。
- (void)updateTrackingAreas {
[super updateTrackingAreas];
NSTrackingArea *const trackingArea = [[NSTrackingArea alloc] initWithRect:NSZeroRect options:(NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways | NSTrackingInVisibleRect) owner:self userInfo:nil];
[self addTrackingArea:trackingArea];
}
然后繼續實現鼠標移入移出的方法:
- (void)mouseEntered:(NSEvent *)event {
[super mouseEntered:event];
self.mouseInside = YES;
[self setNeedsDisplayForStandardWindowButtons];
}
- (void)mouseExited:(NSEvent *)event {
[super mouseExited:event];
self.mouseInside = NO;
[self setNeedsDisplayForStandardWindowButtons];
}
在這里我們需要實現一個在蘋果的官方文檔中并不存在的方法- _mouseInGroup:
,這個方法用來返回判斷鼠標是否在視圖中的參數。
- (BOOL)_mouseInGroup:(NSButton *)button {
return self.mouseInside;
}
最后一個就是需要重繪一下按鈕的狀態:
- (void)setNeedsDisplayForStandardWindowButtons {
[self.closeButton setNeedsDisplay];
[self.minitButton setNeedsDisplay];
[self.zoomButton setNeedsDisplay];
}
這樣我們就實現了自定義的window button,但是窗口在成為第一響應者和不適第一響應者的時候,還有當點擊最小化的時候等等情況下,window button都會處于錯誤的狀態中,例如,當失去第一響應的時候,這三個按鈕應該變為淡灰色,但卻是彩色,當點擊最小化,然后再點擊顯示窗口的時候,三個按鈕還是停留在鼠標移入的狀態中,所以這些情況都要進行處理。這里我用通知實現了相關的處理:
首先,注冊通知:
NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
[defaultCenter addObserver:self selector:@selector(applicationWillBecomeActive:)
name:NSApplicationWillBecomeActiveNotification object:NSApp];
[defaultCenter addObserver:self selector:@selector(applicationDidResignActive:)
name:NSApplicationDidResignActiveNotification object:NSApp];
[defaultCenter addObserver:self selector:@selector(windowActiveChanged:)
name:NSWindowDidBecomeMainNotification object:nil];
然后實現處理方法(如果有更好的處理方式,大家可以自行寫邏輯,這里僅供參考):
- (void)applicationWillBecomeActive:(NSNotification *)notification {
self.mouseInside = YES;
[self setNeedsDisplayForStandardWindowButtons];
self.mouseInside = NO;
[self setNeedsDisplayForStandardWindowButtons];
}
- (void)applicationDidResignActive:(NSNotification *)notification {
self.mouseInside = NO;
[self setNeedsDisplayForStandardWindowButtons];
}
- (void)windowActiveChanged:(NSNotification *)notification {
self.mouseInside = NO;
[self setNeedsDisplayForStandardWindowButtons];
}
這樣左邊的三個自定義window button就已經完成。
兼容10.10之前的按鈕需要實現自定義全屏按鈕(NSWindowFullScreenButton)
創建全屏按鈕的方式與之前相同,不多說直接看代碼:
if (self.versionNum != NSNotFound) {
self.fullScreenButton = [NSWindow standardWindowButton:NSWindowFullScreenButton forStyleMask:self.window.styleMask];
[self.fullScreenButton setFrame:NSMakeRect(0, 0, self.fullScreenButton.frame.size.width, self.fullScreenButton.frame.size.height)];
[self.topView.fullScreenBackView addSubview:self.fullScreenButton];
}
在這里我判斷了一下系統的版本。
注意
設置按鈕一定要在主視圖控制器中進行,不能在自定義的子視圖中進行,否則在10.10之前的系統中會出現coreUI的錯誤提示。而且這個錯誤出現的幾率不是100%
在全屏之后,我們最好將所有的window button給隱藏掉,調用setHidden:
即可。
這下,自定義的window button就完成了。