2011-12-05 13 views
6

sto disegnando un rettangolo su una sottoclasse personalizzata di NSView che possono poi essere trascinato all'interno dei confini della vista:Trascinando un rettangolo nel cacao

enter image description here

Il codice per fare questo è:

// Get the starting location of the mouse down event. 
NSPoint location = [self convertPoint: [event locationInWindow] fromView: nil]; 

// Break out if this is not within the bounds of the rect. 
if (!NSPointInRect(location, [self boundsOfAllControlPoints])) { 
    return; 
} 

while (YES) { 

    // Begin modal mouse tracking, looking for mouse dragged and mouse up events 
    NSEvent *trackingEvent = [[self window] nextEventMatchingMask:(NSLeftMouseDraggedMask | NSLeftMouseUpMask)]; 

    // Get tracking location and convert it to point in the view. 
    NSPoint trackingLocation = [self convertPoint:[trackingEvent locationInWindow] fromView:nil]; 

    // Calculate the delta's of x and y compared to the previous point. 
    long dX = location.x - trackingLocation.x; 
    long dY = location.y - trackingLocation.y; 

    // Update all points in the rect 
    for (int i = 0; i < 4; i++) { 
     NSPoint newPoint = NSMakePoint(points[i].x - dX, points[i].y - dY); 
     points[i] = newPoint; 
    } 

    NSLog(@"Tracking location x: %f y: %f", trackingLocation.x, trackingLocation.y); 

    // Set current location as previous location. 
    location = trackingLocation; 

    // Ask for a redraw. 
    [self setNeedsDisplay:YES]; 

    // Stop mouse tracking if a mouse up is received. 
    if ([trackingEvent type] == NSLeftMouseUp) { 
     break; 
    } 

} 

Prendo praticamente un evento del mouse verso il basso e controlla se la sua posizione si trova all'interno del rettangolo trascinabile. Se lo è, comincio a seguire il movimento del mouse in trackingEvent. Calcolo le delta per le coordinate x e y, creo nuovi punti per il rage trascinabile e richiedo un aggiornamento delle visualizzazioni delle viste.

Anche se funziona, sembra un po '"amatoriale", poiché durante il trascinamento, il puntatore del mouse raggiungerà la forma trascinata e alla fine attraverserà i suoi bordi. In altre operazioni di trascinamento si vedrà il puntatore del mouse fissato sulla posizione dell'oggetto trascinato, dall'inizio alla fine dell'operazione di trascinamento.

Che cosa sta causando questo effetto?

EDIT:

ho cambiato il mio approccio seguente risposta di Rob e ha adottato l'approccio in tre modalità:

- (void) mouseDown: (NSEvent*) event { 

    // There was a mouse down event which might be in the thumbnail rect. 

    [self setDragStartPoint: [self convertPoint: [event locationInWindow] fromView: nil]]; 

    // Indicate we have a valid start of a drag. 
    if (NSPointInRect([self dragStartPoint], [self boundsOfAllControlPoints])) { 
     [self setValidDrag: YES]; 
    } 

} 

- (void) mouseDragged: (NSEvent *) anEvent { 

    // Return if a valid drag was not detected during a mouse down event. 
    if (![self validDrag]) { 
     return; 
    } 

    NSLog(@"Tracking a drag."); 

    // Get tracking location and convert it to point in the view. 
    NSPoint trackingLocation = [self convertPoint: [anEvent locationInWindow] fromView: nil]; 

    // Calculate the delta's of x and y compared to the previous point. 
    long dX = [self dragStartPoint].x - trackingLocation.x; 
    long dY = [self dragStartPoint].y - trackingLocation.y; 

    // Update all points in the rect 
    for (int i = 0; i < 4; i++) { 
     NSPoint newPoint = NSMakePoint(points[i].x - dX, points[i].y - dY); 
     points[i] = newPoint; 
    } 

    // Ask for a redraw. 
    [self setNeedsDisplay:YES]; 

    NSLog(@"Tracking location x: %f y: %f", trackingLocation.x, trackingLocation.y); 

    // Set current location as previous location. 
    [self setDragStartPoint: trackingLocation]; 

    NSLog(@"Completed mouseDragged method. Allow for repaint."); 

} 

- (void) mouseUp: (NSEvent *) anEvent { 

    // End the drag. 
    [self setValidDrag: NO]; 
    [self setNeedsDisplay: YES]; 

} 

Anche se l'effetto è un po 'meglio, c'è ancora un notevole ritardo con il rect alla fine trascinando dietro la direzione del movimento del puntatore del mouse. Ciò è particolarmente evidente quando sposto il mouse lentamente durante un trascinamento.

MODIFICA 2:

Capito. Il problema era con il calcolo dei delta. L'ho usato a lungo mentre dovevo usare il float. Funziona alla grande ora.

+1

Grazie per aver condiviso i tuoi codici per ottenere il trascinamento del mouse. – Tommy

risposta

9

Stai trattenendo il ciclo degli eventi durante il sorteggio, il che significa che il quadrato non può mai ridisegnare. La tua chiamata a setNeedsDisplay: non disegna nulla. Contrassegna semplicemente la vista per essere ridisegnata. Non può ridisegnare fino a quando questa routine non ritorna, cosa che non si fa fino a quando il pulsante del mouse non viene rilasciato.

Leggi Handling Mouse Dragging Operations per una discussione completa su come implementare il trascinamento in Cocoa. È necessario tornare da mouseDown: e ignorare mouseDragged: e mouseUp: oppure è necessario pompare il ciclo degli eventi in modo che il ciclo di disegno possa essere elaborato.

Tendo a raccomandare il primo approccio, anche se richiede più metodi. Il pompaggio del ciclo degli eventi può creare bug molto sorprendenti e deve essere usato con cautela. I bug più comuni nella mia esperienza sono dovuti al fatto che i selettori ritardati si attivano quando si pompa il ciclo degli eventi, causando l'esecuzione di un codice "extra" nel corso della routine di trascinamento. In alcuni casi, ciò può causare reentrance e deadlock. (Ho avuto questo accada ...)

+0

+1 - definisci decisamente il primo approccio menzionato. – bryanmac

Problemi correlati