OSX下原生態按鈕的Hover狀態

OSX原生態按鈕的Hover狀態

        在上一篇《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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章