2012-09-18 13 views
9

He intentado dibujar NSButton personalizados, pero parece que estoy reinventando la rueda aquí. ¿Hay alguna manera de simplemente reemplazar las imágenes predeterminadas utilizadas para los botones de cerrar, minimizar y ampliar?Cómo dibujar controles de ventana personalizados (cerrar, minimizar y ampliar botones)

Varias aplicaciones ya lo hacen:

  • OSX 10.8 de aplicación Recordatorios (que aparecen en gris oscuro cuando la ventana no es la clave, frente a la mayoría parece gris claro)
  • Tweetbot (Todos los botones tienen un aspecto totalmente personalizada)

Más información:

puedo generar los valores predeterminados del sistema, tales como standardWindowButton:NSWindowCloseButton. Pero a partir de ahí, el setter setImage no cambia la apariencia de los botones.

Respuesta

26

Editar: Desde que escribí esto, INAppStore ha implementado una forma bastante agradable de hacerlo con INWindowButton. Si está buscando una solución de arrastrar y soltar, consulte allí, pero el código siguiente aún lo ayudará a implementar el suyo propio.


Así que no pude encontrar una manera de alterar el standardWindowButton s. Aquí hay un tutorial de cómo creé mis propios botones.

Nota: Hay 4 estados de los botones pueden estar en

  • ventana inactiva Window Inactive Controls
  • ventana activa - Normal Window Active Normal Controls
  • ventana activa - ciernen Window Active Hover Controls
  • ventana activa - pulse Window Active Press Controls

¡En el tutorial!

Paso 1: ocultar los botones preexistentes

NSButton *windowButton = [self standardWindowButton:NSWindowCloseButton]; 
[windowButton setHidden:YES]; 
windowButton = [self standardWindowButton:NSWindowMiniaturizeButton]; 
[windowButton setHidden:YES]; 
windowButton = [self standardWindowButton:NSWindowZoomButton]; 
[windowButton setHidden:YES]; 

Paso 2: Configuración de la vista en el Interface Builder

Usted notará Al pasar por encima de los botones de todo cambio en su vuelo estacionario estado, por lo que necesitamos una vista de contenedor para recoger el vuelo estacionario.

  • crear una vista recipiente para ser 54px de ancho x 16px de alto.
  • Crea 3 estilo cuadrado NSButton s, cada uno de 14px de ancho x 16px alto dentro de la vista del contenedor.
  • Coloque los botones de manera que haya espacios de 6px intermedios.

Configuración de los botones

  • En el inspector de atributos, establezca la propiedad Image para cada botón a la ventana de imagen activa de lo normal.
  • Establezca la propiedad de imagen Alternate en la imagen de ventana activa-prensa.
  • Turn Bordered off.
  • Establezca Type en Momentary Change.
  • Para cada botón establezca el identificador a close, minimize o zoom (continuación verá cómo se puede utilizar esto para hacer más simple la subclase NSButton)

Paso 3: subclase la vista contenedora & botones

envase:

Crear un nuevo archivo, subclase NSView. Aquí vamos a usar el Centro de notificaciones para indicarles a los botones cuándo deberían cambiar a su estado de desplazamiento.

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];   
} 

Botones:

Crear un nuevo archivo, esta vez subclase NSButton. Este es un poco más para explicar, así que solo publicaré todo el código.

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 

En IB conjunto de la clase personalizada de la vista de contenedor y los 3 botones a sus respectivas clases que acabamos de crear.

Paso 4: Establecer las acciones de los botones

Estos métodos, llamados desde el controlador de vista, son los mismos que el standardWindowButton s'. Enlázalos a los botones en 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]; 
} 

Paso 5: Añadir la vista a la ventana

Tengo una configuración de controlador de xib y vista separada específicamente para los controles de ventana. El controlador de vista se llama 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]; 

Espero que esto ayude. Esta es una publicación bastante larga, si crees que cometí un error o dejé algo, por favor avísame.

+1

Esta no es una publicación larga, es una ** excelente ** respuesta. Marcado como referencia futura. – sosborn

+0

@sosborn gracias, agradezco los comentarios :) –

+0

Esta publicación no trata problemas como la accesibilidad y el comportamiento del primer mouse. Estos son difíciles de hacer bien ... ¡lo mejor es usar los botones del sistema! (Tenga en cuenta que a partir de Yosemite, las barras de herramientas y las barras de título de la ventana se pueden combinar y los botones de la ventana se centrarán verticalmente.) – jtbandes

Cuestiones relacionadas