如何绘制自定义窗口控件(关闭,最小化和缩放按钮) [英] How to draw custom window controls (close, minimize, and zoom buttons)

查看:879
本文介绍了如何绘制自定义窗口控件(关闭,最小化和缩放按钮)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经尝试绘制自定义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 the setImage 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 standardWindowButtons. 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 NSButtons, 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 to Momentary Change.
    • For each button set the identifier to close,minimize or zoom (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 standardWindowButtons'. 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆