Crea una subclase de NSPopUpButton
y anula los eventos mouseDown
/mouseUp
.
Tienen la demora de evento mouseDown
por un momento antes de llamar a la implementación de super
y solo si el mouse aún se mantiene presionado.
tienen el evento mouseUp
establece la selectedMenuItem
a nil
(y por lo tanto habrá selectedMenuItemIndex
-1
) antes de disparar el botón de target
/action
.
El único otro problema es manejar clics rápidos, donde el temporizador de un clic puede disparar en el momento en que el mouse está apagado para un clic futuro. En lugar de usar un NSTimer
e invalidarlo, elegí tener un contador simple para los eventos mouseDown
y rescatarme si el contador ha cambiado.
Aquí está el código que estoy utilizando en mi subclase:
// MyClickAndHoldPopUpButton.h
@interface MyClickAndHoldPopUpButton : NSPopUpButton
@end
// MyClickAndHoldPopUpButton.m
@interface MyClickAndHoldPopUpButton()
@property BOOL mouseIsDown;
@property BOOL menuWasShownForLastMouseDown;
@property int mouseDownUniquenessCounter;
@end
@implementation MyClickAndHoldPopUpButton
// highlight the button immediately but wait a moment before calling the super method (which will show our popup menu) if the mouse comes up
// in that moment, don't tell the super method about the mousedown at all.
- (void)mouseDown:(NSEvent *)theEvent
{
self.mouseIsDown = YES;
self.menuWasShownForLastMouseDown = NO;
self.mouseDownUniquenessCounter++;
int mouseDownUniquenessCounterCopy = self.mouseDownUniquenessCounter;
[self highlight:YES];
float delayInSeconds = [NSEvent doubleClickInterval];
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
if (self.mouseIsDown && mouseDownUniquenessCounterCopy == self.mouseDownUniquenessCounter) {
self.menuWasShownForLastMouseDown = YES;
[super mouseDown:theEvent];
}
});
}
// if the mouse was down for a short enough period to avoid showing a popup menu, fire our target/action with no selected menu item, then
// remove the button highlight.
- (void)mouseUp:(NSEvent *)theEvent
{
self.mouseIsDown = NO;
if (!self.menuWasShownForLastMouseDown) {
[self selectItem:nil];
[self sendAction:self.action to:self.target];
}
[self highlight:NO];
}
@end
Lástima que no puede establecer la altura de personalización para NSSegmentedControl - Necesito ese menú adjunto a un botón grande. – zrxq
¡Funcionó a la perfección! ¡Gracias! – Alex
Buen truco, con un poco de juego en IB puedes obtener un control realmente elegante como este. –