在可可中拖动一个矩形

Posted

技术标签:

【中文标题】在可可中拖动一个矩形【英文标题】:Dragging a rectangle in cocoa 【发布时间】:2012-01-13 15:10:23 【问题描述】:

我正在 NSView 的自定义子类上绘制一个矩形,然后可以在视图的边界内拖动它:

执行此操作的代码是:

    // 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;
    


我基本上捕捉到鼠标按下事件并检查它的位置是否在可拖动的矩形内。如果是,我开始在 trackingEvent 中跟踪鼠标的移动。我计算 x 和 y 坐标的增量,为可拖动的矩形创建新点,并请求刷新视图显示。

虽然它有效,但它看起来有点“业余”,因为在拖动过程中,鼠标指针会赶上被拖动的形状并最终越过它的边界。在其他拖动操作中,从拖动操作的开始到结束,您会看到鼠标指针固定在被拖动对象的位置。

造成这种影响的原因是什么?

编辑:

我已经按照 Rob 的回答改变了我的方法,并采用了三种方法:

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


虽然效果稍微好一些,但由于矩形最终拖到鼠标指针的移动方向后面,仍然存在明显的延迟。当我在拖动过程中缓慢移动鼠标时,这一点尤其明显。

编辑 2:

知道了。问题在于计算增量。我用了 long ,而我应该使用 float。现在效果很好。

【问题讨论】:

感谢分享实现鼠标拖动的代码。 【参考方案1】:

您在绘制过程中保持事件循环,这意味着正方形永远不会重绘。您对setNeedsDisplay: 的调用不会吸引任何东西。它只是标记要重绘的视图。在此例程返回之前,它不能重绘,直到释放鼠标按钮才能重绘。

阅读Handling Mouse Dragging Operations 以获取有关如何在 Cocoa 中实现拖动的完整讨论。您要么需要从mouseDown: 返回并覆盖mouseDragged:mouseUp:,要么您需要自己泵送事件循环以便绘图循环可以处理。

我倾向于推荐第一种方法,即使它需要多种方法。抽出事件循环可能会产生非常令人惊讶的错误,应谨慎使用。在我的经验中,最常见的错误是由于在您泵送事件循环时延迟选择器触发,导致“额外”代码在您的拖动例程中间运行。在某些情况下,这可能会导致重入和死锁。 (我遇到过这种情况......)

【讨论】:

以上是关于在可可中拖动一个矩形的主要内容,如果未能解决你的问题,请参考以下文章

如何通过按下和拖动在 Qt Quick/QML 画布中绘制一个矩形

在 windows phone 7.5 中使用拖动事件在网格中切换矩形

如何使用C#实现可拖动的透明矩形框/窗体

拖动并缩放使用 UIBezierPath IOS/Swift 绘制的矩形

画一个可拖动的矩形

SVG:拖动矩形仅在缓慢拖动时有效