Estoy intentando crear una subclase NSSlider para crear un control llamado jog dial. Básicamente lo que necesito es un control deslizante que siempre comienza en el medio y cuando se mueve hacia la izquierda o hacia la derecha enviará notificaciones de vez en cuando (determinado por un atributo que puede establecerse) informando a su contenedor de su valor actual, luego cuando dejarte ir por la perilla, volverá al medio. Esperaba implementar la funcionalidad para devolver el control deslizante al centro y dejar de enviar notificaciones en el evento mouseUp del control deslizante pero parece que, por alguna razón, Apple desactiva el evento MouseUp después de un evento mouseDown en el control deslizante y maneja toda la funcionalidad del control deslizante en un nivel inferior ¿Hay alguna forma de recuperar el evento mouseUp? Si no, ¿alguien puede sugerir una solución razonable?Subclase NSSlider: necesita una solución alternativa para los eventos faltantes del mouse (Cocoa OSX)
Respuesta
Siempre que detecte que la aplicación de una superclase de mouseDragged:
o mouseUp:
no está recibiendo llamadas, es más probable debido a la aplicación de mouseDown:
de la clase entra en un bucle de seguimiento. Esto es ciertamente cierto para muchas subclases NSControl
incluyendo NSSlider
.
Una mejor forma de detectar un mouse es subclase de la celda e ignorar el método de seguimiento apropiado. En este caso, probablemente desee - (void)stopTracking:(NSPoint)lastPoint at:(NSPoint)stopPoint inView:(NSView *)controlView mouseIsUp:(BOOL)flag
, pero las variantes startTracking:
y continueTracking:
también pueden resultar útiles para lo que está intentando hacer.
Hay un truco que utilizo (pero no inventé) para tales situaciones. Primero, en IB, designe el control deslizante como "continuo", de modo que obtendrá mensajes de acción a medida que se mueve el control deslizante. Luego, en el método de acción, hacer esto:
[NSObject cancelPreviousPerformRequestsWithTarget: self
selector: @selector(finishTrack) object: nil ];
[self performSelector: @selector(finishTrack) withObject: nil
afterDelay: 0.0];
Después de soltar el ratón, se llamará al método finishTrack
.
Parece que la idea de Kperryua proporcionaría la solución más limpia, así que lo marcaré como la respuesta aceptada, pero terminé usando un truco que funcionó para mi situación específica, así que pensé que podría compartir eso también.
La aplicación que estoy creando necesita ser multiplataforma, así que estoy usando Cocotron (un proyecto de código abierto que implementa gran parte de Cocoa API en Windows) para lograr este objetivo y cocotron no es compatible con stopTracking: ... método mencionado anteriormente.
Afortunadamente, mientras jugábamos con un pequeño programa de prueba, descubrimos que si anula el método MouseSnow del NSSlider y llama al súper de ese método, no regresa hasta que se suelta el botón del mouse, entonces solo puede poner el código que debe estar en mouseUp dentro del método mouseDown después de la llamada a super. Esto es un poco sucio, pero parece ser la única solución que funcionará en nuestro caso, así que vamos a tener que ir con eso.
Esto funciona para mí (y es más fácil que la subclasificación NSSlider):
- (IBAction)sizeSliderValueChanged:(id)sender {
NSEvent *event = [[NSApplication sharedApplication] currentEvent];
BOOL startingDrag = event.type == NSLeftMouseDown;
BOOL endingDrag = event.type == NSLeftMouseUp;
BOOL dragging = event.type == NSLeftMouseDragged;
NSAssert(startingDrag || endingDrag || dragging, @"unexpected event type caused slider change: %@", event);
if (startingDrag) {
NSLog(@"slider value started changing");
// do whatever needs to be done when the slider starts changing
}
// do whatever needs to be done for "uncommitted" changes
NSLog(@"slider value: %f", [sender doubleValue]);
if (endingDrag) {
NSLog(@"slider value stopped changing");
// do whatever needs to be done when the slider stops changing
}
}
Esto se puede hacer solamente por subclases (como métodos de @mprudhom no funcionarán al 100% para la liberación know)
para el inicio de la resistencia al avance, es necesario coger becomeFirstResponder
(para ello es necesario para proporcionar también needsPanelToBecomeKey
)
para el fin de arrastrar, necesita subclase mouseDown:
(la llamada mouseDown, pero se pone la calle d cuando la perilla liberado)
Nota: este enfoque hará que su deslizador del primer nivel de respuesta, la cancelación de cualquier otra corriente de primer nivel de respuesta
Nota: aproximación desde mprudhom
@implementation CustomSlider
- (void)mouseDown:(NSEvent *)theEvent {
[super mouseDown:theEvent];
NSLog(@"OK");
}
- (BOOL)needsPanelToBecomeKey {
[super needsPanelToBecomeKey];
return YES;
}
- (BOOL)becomeFirstResponder {
[super becomeFirstResponder];
NSLog(@"Became first responder.");
return YES;
}
@end
Por si alguien se pregunta cómo para lograr esto en Swift 3 con un NSSlider eso es estado se establece en continuo:
@IBOutlet weak var mySlider: NSSlider!
...
@IBAction func handlingNSSliderChanges(_ sender: Any) {
// Perform updates on certain events
if sender is NSSlider{
let event = NSApplication.shared().currentEvent
if event?.type == NSEventType.leftMouseUp {
print("LeftMouseUp event inside continuous state NSSlider")
}
}
// Do continuous updates
if let value = mySlider?.integerValue{
demoLabel.stringValue = String(value)
}
}
...
que tenían una necesidad similar, pero para un control deslizante en una NSTouchBar . Utilicé un enfoque similar "rápido y sucio" como Marc Prud'hommeaux y Martin Majewski, eventos ligeramente diferentes para inspeccionar. Aquí está el código Swift que le permite rastrear tanto el valor continuo como el valor final del control deslizante en una barra táctil.
func sliderValueChanged(_ sender: NSSlider) {
let doubleValue = sender.doubleValue
Swift.print("Continuous value: \(doubleValue)")
// find out if touch event has ended
let event = NSApplication.shared().currentEvent
if event?.type == NSEventType.directTouch {
if let endedTouches = event?.touches(matching: .ended, in: nil) {
if (endedTouches.count > 0) {
Swift.print("The final value was: \(doubleValue)")
}
}
}
}
- 1. Eventos de Mouse Moved Global en Cocoa
- 2. Eventos globales del mouse
- 3. Consumir eventos de mouse/trackpad OSX con un evento toque
- 4. Mac OSX Java: Recibir eventos del mouse cuando no está activado
- 5. Una solución alternativa para SSL en Heroku
- 6. Controles/widgets personalizados Cocoa OSX
- 7. Posible? - Una superposición div que los eventos del mouse ignoran por completo (para que los eventos del mouse afecten solo al div a continuación)
- 8. ¿Coordenadas de mouse de alta resolución y alto framerate en OSX? (¿O alguna otra solución?)
- 9. Manejar todos los eventos del mouse en Android
- 10. ¿Cómo puedo simular los eventos del mouse desde el código?
- 11. ¿Ocultar globalmente el cursor del mouse en Cocoa/Carbon?
- 12. Solución alternativa para necesitar una fila _id para CursorAdapter
- 13. Solución para setToolTipText consumir eventos de ratón?
- 14. Manejo de eventos del mouse con cvSetMouseCallback
- 15. ¿Cómo puedo hacer que una ventana sea invisible para los eventos del mouse en WPF?
- 16. Acceso directo a eventos de teclado en OSX
- 17. Controlador global de eventos del mouse
- 18. ¿Cómo puedo usar la sincronización de JavaScript para controlar los eventos de detención del mouse y del mouse?
- 19. ¿Puedo usar Python para capturar eventos de teclado y mouse en OSX?
- 20. Eventos GWT del lado del servidor; alternativa a Vaadin
- 21. Solución alternativa para WPF ¿Error Freezable?
- 22. necesita una solución para la consulta sql utilizando el pivote
- 23. MouseListener para JPanel mouse missing Eventos clonados
- 24. mejor solución alternativa para ASP.NET AJAX UpdatePanel
- 25. ¿cuál es la solución alternativa para paymentWithProductIdentifier?
- 26. ¿Cómo se interceptan los eventos del mouse de una ventana transparente?
- 27. Qt4: eventos del botón del mouse de QTableView no detectados
- 28. Cocoa: Obteniendo la posición actual del mouse en la pantalla
- 29. Solución alternativa para algunas características WPF que faltan en Silverlight
- 30. Google Chrome "window.open" ¿solución alternativa?
esto no funciona en todos los casos (comience a arrastrar y vaya al menú, nunca registrará la terminaciónDrag) –