如何绘制自定义窗口控件(关闭,最小化和缩放按钮) [英] How to draw custom window controls (close, minimize, and zoom buttons)
问题描述
我已经尝试绘制自定义NSButtons,但它似乎是我重新发明的轮子。有没有办法只是替换用于关闭,最小化和缩放按钮的默认图片?
几个应用程序已经做到了:
- OSX 10.8的提醒应用程式(当窗口不重要时显示为深灰色,vs大多数显示为浅灰色)
- 所有按钮完全自定义)
更多信息:
系统默认为 standardWindowButton:NSWindowCloseButton
。但是 setImage
setter不会改变按钮的外观。
编辑:自从我写这篇文章后, INAppStore 已实施用 INWindowButton
来做这个很好的方法。如果你正在寻找一个拖放解决方案检查那里,但下面的代码仍然会帮助你实现自己的。
所以我找不到一种方法来改变 standardWindowButton
。
注意:
有4种状态b$ b
- 窗口无效
-
- 视窗启用 - 正常
- 视窗启用 - 悬停
- 视窗active-press
进入演练!
步骤1:隐藏预先存在的按钮
NSButton * windowButton = [self standardWindowButton:NSWindowCloseButton];
[windowButton setHidden:YES];
windowButton = [self standardWindowButton:NSWindowMiniaturizeButton];
[windowButton setHidden:YES];
windowButton = [self standardWindowButton:NSWindowZoomButton];
[windowButton setHidden:YES];
步骤2:在Interface Builder中设置视图 b
$ b
您会注意到,悬停时,这些按钮都会更改为悬停状态,因此我们需要一个容器视图来拾取悬停。
- 创建容器视图为54px宽x 16px高。
- 创建3方形风格
NSButton
-
设置按钮
- 在属性检查器中,设置
- 将
替代
- 设置 code>键入到
瞬间更改
。 - 每个按钮将标识符设置为
关闭
,最小化
或缩放
可以使用它来使NSButton子类更简单)
步骤3:
NSView。
HMTrafficLightButtonsContainer.m
在这里,我们将使用通知中心来通知按钮何时切换到悬停状态。
//告诉视图拾取悬停事件
- (void)viewDidMoveToWindow {
[self addTrackingRect: self bounds]
owner:self
userData:nil
assumeInside:NO];
}
//当鼠标进入/退出时,我们发送这些通知
- (void)mouseEntered:(NSEvent *)theEvent {
[[NSNotificationCenter defaultCenter ] postNotificationName:@HMTrafficButtonMouseEnterobjects:self];
}
- (void)mouseExited:(NSEvent *)theEvent {
[[NSNotificationCenter defaultCenter] postNotificationName:@HMTrafficButtonMouseExitobject:self];
}
按钮:
创建一个新文件,这个时候是子类NSButton。
HMTrafficLightButton.m
这是一个更好的解释。
@implementation HMTrafficLightButton {
NSImage * inactive;
NSImage * active;
NSImage * hover;
NSImage * press;
BOOL activeState;
BOOL hoverState;
BOOL pressedState;
}
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if(self){
[self setup];
}
return self;
}
- (id)initWithFrame:(NSRect)frameRect {
self = [super initWithFrame:frameRect];
if(self){
[self setup];
}
return self;
}
- (void)setup {
//设置映像,我们使用标识符来选择要加载的映像
active = [NSImage imageNamed:[NSString stringWithFormat:@window-button - %@ - active,self.identifier]];
hover = [NSImage imageNamed:[NSString stringWithFormat:@window-button - %@ - hover,self.identifier]];
press = [NSImage imageNamed:[NSString stringWithFormat:@window-button - %@ - press,self.identifier]];
inactive = [NSImage imageNamed:@window-button-all-inactive];
//当调用
if([self.window isMainWindow]&& [NSApplication sharedApplication] isActive]时,检查窗口是否处于活动或非活动状态。 {
[self setActiveState];
} else {
[self setInactiveState];
}
//观察容器视图中的悬停通知
//还监视窗口
//成为/ resigns时的通知main
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(setActiveState)
name:NSWindowDidBecomeMainNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
选择器:@selector(setInactiveState)
name:NSWindowDidResignMainNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(hoverIn)
name:@HMTrafficButtonMouseEnter
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(hoverOut)
name:@HMTrafficButtonMouseExit
object:nil];
}
- (void)mouseDown:(NSEvent *)theEvent {
pressedState = YES;
hoverState = NO;
[super mouseDown:theEvent];
}
- (void)mouseUp:(NSEvent *)theEvent {
pressedState = NO;
hoverState = YES;
[super mouseUp:theEvent];
}
- (void)setActiveState {
activeState = YES;
if(hoverState){
[self setImage:hover];
} else {
[self setImage:active];
}
}
- (void)setInactiveState {
activeState = NO;
[self setImage:inactive];
}
- (void)hoverIn {
hoverState = YES;
[self setImage:hover];
}
- (void)hoverOut {
hoverState = NO;
if(activeState){
[self setImage:active];
} else {
[self setImage:inactive];
}
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
@end
。
步骤4:设置按钮操作
这些方法,从视图控制器调用,与
standardWindowButton
s相同。将它们链接到IB中的按钮。- (IBAction)clickCloseButton:(id)sender {
[self。 view.window close];
}
- (IBAction)clickMinimizeButton:(id)sender {
[self.view.window miniaturize:sender];
}
- (IBAction)clickZoomButton:(id)sender {
[self.view.window zoom:sender];
}
第5步:将视图添加到窗口
我有一个单独的xib和视图控制器设置专门为窗口控件。视图控制器称为HMWindowControlsController
(HMWindowControlsController *)windowControlsController = [[HMWindowControlsController alloc] initWithNibName:@WindowControls bundle:nil];
NSView * windowControlsView = windowControlsController.view;
//设置窗口控件的位置,x是7 px,y将
//取决于您的标题栏高度。
windowControlsView.frame = NSMakeRect(7.0,10.0,54.0,16.0);
//添加到目标视图
[targetView addSubview:windowControlsView];
希望这有帮助。这是一个很长的帖子,如果你认为我犯了一个错误或留下了东西,请让我知道。
I've made an attempt to draw custom NSButtons, but it seems I'm reinventing the wheel here. Is there a way to just replace the default images used for the close, minimize and zoom buttons?
Several apps already do it:
- OSX 10.8's Reminders app (they appear dark grey when the window is not key, vs most appear light grey)
- Tweetbot (All buttons look totally custom)
More info:
I can generate the system defaults as such
standardWindowButton:NSWindowCloseButton
. But from there thesetImage
setter doesn't change the appearance of the buttons.解决方案Edit: Since I wrote this, INAppStore has implemented a pretty nice way to do this with
INWindowButton
. If you're looking for a drag and drop solution check there, but the code below will still help you implement your own.
So I couldn't find a way to alter the
standardWindowButton
s. Here is a walkthrough of how I created my own buttons.Note: There are 4 states the buttons can be in
- Window inactive
- Window active - normal
- Window active - hover
- Window active - press
On to the walkthrough!
Step 1: Hide the pre-existing buttons
NSButton *windowButton = [self standardWindowButton:NSWindowCloseButton]; [windowButton setHidden:YES]; windowButton = [self standardWindowButton:NSWindowMiniaturizeButton]; [windowButton setHidden:YES]; windowButton = [self standardWindowButton:NSWindowZoomButton]; [windowButton setHidden:YES];
Step 2: Setup the view in Interface Builder
You'll notice on hover the buttons all change to their hover state, so we need a container view to pick up the hover.
- Create a container view to be 54px wide x 16px tall.
- Create 3 Square style
NSButton
s, each 14px wide x 16px tall inside the container view. - Space out the buttons so there is are 6px gaps in-between.
Setup the buttons
- In the attributes inspector, set the
Image
property for each button to the window-active-normal image. - Set the
Alternate
image property to the window-active-press image. - Turn
Bordered
off. - Set the
Type
toMomentary Change
. - For each button set the identifier to
close
,minimize
orzoom
(Below you'll see how you can use this to make the NSButton subclass simpler)
Step 3: Subclass the container view & buttons
Container:
Create a new file, subclass NSView. Here we are going to use Notification Center to tell the buttons when they should switch to their hover state.
HMTrafficLightButtonsContainer.m
// Tells the view to pick up the hover event - (void)viewDidMoveToWindow { [self addTrackingRect:[self bounds] owner:self userData:nil assumeInside:NO]; } // When the mouse enters/exits we send out these notifications - (void)mouseEntered:(NSEvent *)theEvent { [[NSNotificationCenter defaultCenter] postNotificationName:@"HMTrafficButtonMouseEnter" object:self]; } - (void)mouseExited:(NSEvent *)theEvent { [[NSNotificationCenter defaultCenter] postNotificationName:@"HMTrafficButtonMouseExit" object:self]; }
Buttons:
Create a new file, this time subclass NSButton. This one's a bit more to explain so I'll just post all the code.
HMTrafficLightButton.m
@implementation HMTrafficLightButton { NSImage *inactive; NSImage *active; NSImage *hover; NSImage *press; BOOL activeState; BOOL hoverState; BOOL pressedState; } -(id)initWithCoder:(NSCoder *)aDecoder { self = [super initWithCoder:aDecoder]; if (self) { [self setup]; } return self; } - (id)initWithFrame:(NSRect)frameRect { self = [super initWithFrame:frameRect]; if (self) { [self setup]; } return self; } - (void)setup { // Setup images, we use the identifier to chose which image to load active = [NSImage imageNamed:[NSString stringWithFormat:@"window-button-%@-active",self.identifier]]; hover = [NSImage imageNamed:[NSString stringWithFormat:@"window-button-%@-hover",self.identifier]]; press = [NSImage imageNamed:[NSString stringWithFormat:@"window-button-%@-press",self.identifier]]; inactive = [NSImage imageNamed:@"window-button-all-inactive"]; // Checks to see if window is active or inactive when the `init` is called if ([self.window isMainWindow] && [[NSApplication sharedApplication] isActive]) { [self setActiveState]; } else { [self setInactiveState]; } // Watch for hover notifications from the container view // Also watches for notifications for when the window // becomes/resigns main [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(setActiveState) name:NSWindowDidBecomeMainNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(setInactiveState) name:NSWindowDidResignMainNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(hoverIn) name:@"HMTrafficButtonMouseEnter" object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(hoverOut) name:@"HMTrafficButtonMouseExit" object:nil]; } - (void)mouseDown:(NSEvent *)theEvent { pressedState = YES; hoverState = NO; [super mouseDown:theEvent]; } - (void)mouseUp:(NSEvent *)theEvent { pressedState = NO; hoverState = YES; [super mouseUp:theEvent]; } - (void)setActiveState { activeState = YES; if (hoverState) { [self setImage:hover]; } else { [self setImage:active]; } } - (void)setInactiveState { activeState = NO; [self setImage:inactive]; } - (void)hoverIn { hoverState = YES; [self setImage:hover]; } - (void)hoverOut { hoverState = NO; if (activeState) { [self setImage:active]; } else { [self setImage:inactive]; } } - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; } @end
In IB set the Custom Class of the container view and all 3 buttons to their respective classes that we just created.
Step 4: Set the button actions
These methods, called from the view controller, are the same as the
standardWindowButton
s'. Link them to the buttons in IB.- (IBAction)clickCloseButton:(id)sender { [self.view.window close]; } - (IBAction)clickMinimizeButton:(id)sender { [self.view.window miniaturize:sender]; } - (IBAction)clickZoomButton:(id)sender { [self.view.window zoom:sender]; }
Step 5: Add the view to the window
I have a separate xib and view controller setup specifically for the window controls. The view controller is called HMWindowControlsController
(HMWindowControlsController*) windowControlsController = [[HMWindowControlsController alloc] initWithNibName:@"WindowControls" bundle:nil]; NSView *windowControlsView = windowControlsController.view; // Set the position of the window controls, the x is 7 px, the y will // depend on your titlebar height. windowControlsView.frame = NSMakeRect(7.0, 10.0, 54.0, 16.0); // Add to target view [targetView addSubview:windowControlsView];
Hope this helps. This is a pretty lengthy post, if you think I've made a mistake or left something out please let me know.
这篇关于如何绘制自定义窗口控件(关闭,最小化和缩放按钮)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
- 在属性检查器中,设置