[系统控件重绘教程(二)]重绘NSButton
时间: 2010-02-05 10:26 点击:1858 次
[系统控件重绘教程(二)]重绘NSButton
作者 yoyokko
原帖地址 http://www.cocoachina.com/bbs/read.php?tid-14590.html
首先大家看Apple关于NSButton的描述,NSButton跟NSWindow一样,它的外观形式也是委托给NSButtonCell来处理的, 自身只包含逻辑代码。
所以重绘NSButton就是重绘NSButtonCell啦,然后把NSButton的cell设置位你自己的cell就 好了。
1)重绘目标
首 先观察一下系统NSButton的行为和外观表现,可以发现默认Button(快捷健设置为return)是有一个一闪一闪的效果,鼠标点击其他非默认 button的时候同window上默认button的蓝色消失,同时被点中button变成蓝色。放开鼠标,默认button恢复蓝色背景并闪烁,被点 击button变白色。
重绘一个控件最好是不要改变其默认行为,也最好不要违反Apple的关于界面设计的建议文档。所以我们的目标是重绘出来的 button是灰色渐变背景,默认button有一个黄色的圈圈围在周围,不闪烁。被点中的button显示黄色圈圈,默认button黄色圈圈消失。
效 果如下图:
鼠标未按下效果
鼠标按下效果
2)渐变 背景
NSButtonCell的重绘方法很简单,重写下面的方法即可。
逻辑就是
1)检测当前button 的类型(普通button,checkbox,radiobutton等)
2)画button的基本形状和颜色
3)如果当前button 被click了,那么显然的画一个黄色的圈圈上去
4)如果没有被click,那么检测是否为默认button,如果是,并且当前window没有 被click的其他button,那么为自己画一个黄色的圈圈,否则不画。
// buttoncell有一个私有方法来标示当前button的类型
// 这里只列出关心的三种类型
typedef enum KAButtonType{
KACheckBox = 3,
KARadioButton = 4,
KARoundButton = 7
};
- (void)drawWithFrame: (NSRect)cellFrame inView: (NSView *)controlView
{
switch ([self _buttonType]) { // buttonCell的私有函数,可以确定button类型,10.4/10.5/10.6都可用
case KACheckBox:
[self drawCheckInFrame:cellFrame isRadio:NO]; // 画checkbox的形状,这里忽略不画
break;
case KARadioButton:
[self drawCheckInFrame:cellFrame isRadio:YES]; // 画radiobutton的形状,这里忽略不画
break;
default:
switch ([buttonCell bezelStyle]) { // 这就是button啦,默认的形状,这个参数可以在IB里设置,
// 所以button的类型必须为NSRoundedBezelStyle,当然你可以改为其他的
case NSRoundedBezelStyle:
[self drawRoundedButtonInFrame: cellFrame inView: controlView];
break;
case NSRegularSquareBezelStyle:
[self drawHyperLinkButtonInFrame: cellFrame];
break;
default:
break;
}
break;
}
// 画Button的图片哦
// Comment by yoyokko
// if [buttonCell _normalImage] is nil, that to say there is a missing
// field in nib file for this check box -->
// NSButtonCell uses function <(int)_buttonType> to determine button type.
// After hacking, I found that 3==Checkbox, 4==Radio, 7==RoundedButton
if([buttonCell _buttonType] == KARoundButton)
{
if([buttonCell imagePosition] != NSNoImage) {
[self drawImage: [buttonCell image] withFrame: cellFrame inView: [buttonCell controlView]];
}
}
}
// 查询当前window上有没有被click的button
- (void)travelSubViews: (NSView*)view
{
NSArray *items = [view subviews];
NSEnumerator *enumerator = [items objectEnumerator];
id anObject = nil;
while (anObject = [enumerator nextObject])
{
if ([anObject isKindOfClass: [NSButton class]])
{
NSButtonCell *buttonCell = [anObject cell];
NSBezelStyle buttonStyle = [buttonCell bezelStyle];
if ([buttonCell isHighlighted] &&
(buttonStyle == NSRoundedBezelStyle || buttonStyle == NSTexturedRoundedBezelStyle))
{
[self setMIsFound: YES];
break;
}
}
else
{
[self travelSubViews: anObject];
}
}
}
// 画渐变的button和黄色圈圈
-(void)drawRoundedButtonInFrame:(NSRect)frame inView: (NSView *)controlView
{
NSRect textFrame;
//Adjust Rect so strokes are true and
//shadows are visible
frame.origin.x += .5f;
frame.origin.y += .5f;
frame.size.height -= 1;
frame.size.width -= 1;
//Adjust Rect based on ControlSize so that
//my controls match as closely to apples
//as possible.
switch ([buttonCell controlSize]) {
default: // Silence uninitialized variable warnings for textFrame fields.
case NSRegularControlSize:
frame.origin.x += 4;
frame.origin.y += 4;
frame.size.width -= 8;
frame.size.height -= 12;
textFrame = frame;
break;
case NSSmallControlSize:
frame.origin.x += 4;
frame.origin.y += 4;
frame.size.width -= 8;
frame.size.height -= 11;
textFrame = frame;
textFrame.origin.y += 1;
break;
case NSMiniControlSize:
frame.origin.y -= 1;
textFrame = frame;
textFrame.origin.y += 1;
break;
}
//Create Path
NSBezierPath *path = [[NSBezierPath alloc] init];
[path appendBezierPathWithRoundedRect: frame cornerRadius:6.0f];
if([buttonCell isEnabled])
{
// draw inner part of button first
// 画button的灰色渐变部分
[self drawShadingWithStartingColor: [self colorVlaueWithRed: 239 green: 239 blue: 239]//[NSColor blackColor]
withEndingColor: [self colorVlaueWithRed: 93 green: 93 blue: 93]//[NSColor whiteColor]
inBezierPath: path];