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