如何绘制自定义窗口控件(关闭,最小化和缩放按钮)

Ale*_*ant 10 macos cocoa objective-c

我试图绘制自定义NSButton,但似乎我在这里重新发明轮子.有没有办法只替换用于关闭,最小化和缩放按钮的默认图像?

几个应用已经做到了:

  • OSX 10.8的提醒应用程序(当窗口不是键时它们显示为深灰色,而大多数显示为浅灰色)
  • Tweetbot(所有按钮看起来完全自定义)

更多信息:

我可以生成系统默认值standardWindowButton:NSWindowCloseButton.但是从那里,setImage设置器不会改变按钮的外观.

Ale*_*ant 26

编辑:自从我写这篇文章以来,INAppStore已经实现了一个非常好的方法INWindowButton.如果您正在寻找拖放解决方案,请检查那里,但下面的代码仍然可以帮助您实现自己的.


所以我找不到改变standardWindowButtons的方法.这是我如何创建自己的按钮的演练.

注意:按钮可以有4种状态

  • 窗口无效 窗口不活动控件
  • 窗口有效 - 正常 窗口活动正常控制
  • 窗口激活 - 悬停 窗口主动悬停控件
  • 窗口激活 - 按 Window Active Press Controls

在演练中!

第1步:隐藏预先存在的按钮

NSButton *windowButton = [self standardWindowButton:NSWindowCloseButton];
[windowButton setHidden:YES];
windowButton = [self standardWindowButton:NSWindowMiniaturizeButton];
[windowButton setHidden:YES];
windowButton = [self standardWindowButton:NSWindowZoomButton];
[windowButton setHidden:YES];
Run Code Online (Sandbox Code Playgroud)

第2步:在Interface Builder中设置视图

您会注意到,在悬停按钮时,所有按钮都会更改为悬停状态,因此我们需要一个容器视图来拾取悬停.

  • 创建容量视图为54px宽x 16px高.
  • NSButton在容器视图内创建3个Square样式s,每个14px宽x 16px高.
  • 将按钮分开,使中间有6px的间隙.

设置按钮

  • 在属性检查器中,将Image每个按钮的属性设置为窗口活动正常图像.
  • Alternateimage属性设置为window-active-press图像.
  • 开启Bordered关闭.
  • 设置TypeMomentary Change.
  • 对于每个按钮设置标识符close,minimizezoom(下面你将看到如何使用它来使NSButton子类更简单)

第3步:对容器视图和按钮进行子类化

容器:

创建一个新文件,子类NSView.在这里,我们将使用通知中心告诉按钮何时应切换到其悬停状态.

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];        
}
Run Code Online (Sandbox Code Playgroud)

纽扣:

创建一个新文件,这次是子类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 {
    // 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
Run Code Online (Sandbox Code Playgroud)

在IB中,将容器视图的自定义类和所有3个按钮设置为我们刚刚创建的各自类.

第4步:设置按钮操作

从视图控制器调用的这些方法与standardWindowButtons' 相同.将它们链接到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];
}
Run Code Online (Sandbox Code Playgroud)

第5步:将视图添加到窗口

我有一个独立的xib和视图控制器设置专门用于窗口控件.视图控制器称为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];
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助.这是一篇相当冗长的帖子,如果您认为我犯了错误或遗漏了某些内容请告诉我.