在上一篇《OSX10.11分屏(SplitView)功能的新特性研究》文章中,介紹了自定義TitleBar時,用到了原生態的按鈕。此時的原生態按鈕的行爲跟使用原生態的TitleBar上自帶的按鈕行爲還有點不一樣,你把鼠標移到最大化、最小話以及關閉按鈕上的時候,會發現沒有hover的狀態。
究竟是什麼原因呢?我們該如何解決這個問題呢?帶着疑問,查看NSButton的定義,裏面提供的各種函數嘗試一遍都不起多用,此時我們就應該想到了私有類的私有函數,這三個原生態按鈕的cell都繼承於_NSThemeWidgetCell類,查看這個類,最終鎖定了coreUIState這個方法,並且進一步追蹤出三種狀態下返回字符串爲”normal”(正常態)、”rollover”(懸停態)以及”pressed”(按下態)。有關如何鎖定的問題,請參看上篇文章,留給開發者自己實踐去了。
最後使用Method Swizzling 鉤住(Hook)我們想截獲的消息,並對感興趣的消息進一步的處理,代碼片段如下:
//設置標示,便於從截獲的消息中,找出我們感興趣的對象
[self.closeButton setTitle:@"OSX_CUSTOM_BUTTON"];
[self.minimizeButton setTitle:@"OSX_CUSTOM_BUTTON"];
[self.zoomButton setTitle:@"OSX_CUSTOM_BUTTON"];
//消息劫持代碼
CFStringRef (* originalIMP)(id self, SEL _cmd);//保存原始的消息函數
Method coreUIWidgetStateMethod=class_getInstanceMethod(NSClassFromString(@"_NSThemeWidgetCell"), NSSelectorFromString(@"coreUIState"));
const char *encoding=method_getTypeEncoding(coreUIWidgetStateMethod);
originalIMP=(void*)method_getImplementation(coreUIWidgetStateMethod);
class_replaceMethod(NSClassFromString(@"_NSThemeWidgetCell"), NSSelectorFromString(@"coreUIState"), (IMP)methodState, encoding);
//處理劫持的消息
static CFStringRef methodState(id self, SEL _cmd) {
if ([self isKindOfClass:[NSButtonCell class]]) {
NSButtonCell *cell = (NSButtonCell*)self;
NSString *title = [cell title];
if ([title isEqualToString:@"OSX_CUSTOM_BUTTON"]) {
if (cell.highlighted) {
return (__bridge CFStringRef)@"pressed";
} else if (cell.state) {
return (__bridge CFStringRef)@"rollover";
} else {
return (__bridge CFStringRef)@"normal";
}
}
}
//不是我們感興趣的,調用原來的消息處理函數
return originalIMP(self,_cmd);
}
//鼠標移入與移除事件,切換正常態與懸停態
-(void)mouseEntered:(NSEvent *)theEvent {
[self setTitleButtonDisplay:YES];
}
-(void)mouseExited:(NSEvent *)theEvent {
[self setTitleButtonDisplay:NO];
}
- (void)setTitleButtonDisplay:(BOOL)bHover {
//設置state屬性值代表按鈕正常與懸停的狀態
[closeButton setState:bHover];
[zoomButton setState:bHover];
[minimizeButton setState:bHover];
[closeButton setNeedsDisplay:YES];
[zoomButton setNeedsDisplay:YES];
[minimizeButton setNeedsDisplay:YES];
}
將上述代碼移植到自己的工程中,會發現神奇的事情發生了,按鈕可以捕捉到hover狀態了。
轉載請註明出處:http://blog.csdn.net/skynullcode