在可可中拖动一个矩形

Rog*_*ger 6 cocoa drag-and-drop

我在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;
    }

}
Run Code Online (Sandbox Code Playgroud)

我基本上抓住了一个鼠标按下事件并检查它的位置是否在可拖动的矩形内.如果是,我开始在trackingEvent中跟踪鼠标的移动.我计算x和y坐标的delta,为可拖动的rect创建新点,并请求刷新视图显示.

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

是什么导致了这种影响?

编辑:

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

}
Run Code Online (Sandbox Code Playgroud)

尽管效果略好一些,但是最终拖动鼠标指针的移动方向仍然存在明显的延迟.当我在拖动过程中缓慢移动鼠标时,这一点尤为明显.

编辑2:

得到它了.问题在于计算增量.我用了很长时间,但我应该使用浮动.现在很棒.

Rob*_*ier 10

您在绘制期间保持事件循环,这意味着正方形永远不会重绘.你的电话setNeedsDisplay:没有画任何东西.它只标记要重绘的视图.在此例程返回之前,它无法重绘,在释放鼠标按钮之前,您不会执行此操作.

阅读处理鼠标拖动操作以获得有关如何在Cocoa中实现拖动的完整讨论.要么你需要从恢复mouseDown:和覆盖mouseDragged:mouseUp:,或者你需要自己抽事件循环,使绘图周期可以处理.

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