Перетаскивание вида

У меня есть NSView, который я добавляю как подвид другого NSView. Я хочу иметь возможность перетащить первый NSView вокруг родительского вида. У меня есть код, который частично работает, но есть проблема с NSView, движущимся в противоположном направлении по оси Y от перетаскивания моей мыши. (т.е. я тяну вниз, оно идет вверх и наоборот).

Вот мой код:

// -------------------- MOUSE EVENTS ------------------- \\ 

- (BOOL) acceptsFirstMouse:(NSEvent *)e {
    return YES;
}

- (void)mouseDown:(NSEvent *) e { 
    //get the mouse point
    lastDragLocation = [e locationInWindow];
}

- (void)mouseDragged:(NSEvent *)theEvent {
    NSPoint newDragLocation = [theEvent locationInWindow];
    NSPoint thisOrigin = [self frame].origin;
    thisOrigin.x += (-lastDragLocation.x + newDragLocation.x);
    thisOrigin.y += (-lastDragLocation.y + newDragLocation.y);
    [self setFrameOrigin:thisOrigin];
    lastDragLocation = newDragLocation;
}

Представление перевернуто, хотя я вернул его к значению по умолчанию, и, похоже, это не имело значения. Что я делаю неправильно?

Ответ 1

Лучший способ подойти к этой проблеме - начать с прочного понимания координатных пространств.

Во-первых, очень важно понять, что когда мы говорим о "кадре" окна, оно находится в координатном пространстве супервизора. Это означает, что корректировка изменчивости самого представления не будет иметь никакого значения, потому что мы ничего не меняем внутри самого представления.

Но ваша интуиция в том, что переменность важна здесь, верна.

По умолчанию ваш код, как указано, кажется, что он будет работать; возможно, ваш супервизор был перевернут (или не перевернулся), и он находится в другом пространстве координат, чем вы ожидаете.

Вместо того, чтобы случайным образом переворачивать и снимать панорамы, лучше всего преобразовать точки, с которыми вы имеете дело, в известное координатное пространство.

Я отредактировал ваш выше код, чтобы всегда преобразовывать в пространство координат супервизора, потому что мы работаем с источником кадра. Это будет работать, если ваше перетаскиваемое представление будет помещено в перевернутое или не перевернутое представление.

// -------------------- MOUSE EVENTS ------------------- \\ 

- (BOOL) acceptsFirstMouse:(NSEvent *)e {
    return YES;
}

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

    // Convert to superview coordinate space
    self.lastDragLocation = [[self superview] convertPoint:[e locationInWindow] fromView:nil]; 

}

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

    // We're working only in the superview coordinate space, so we always convert.
    NSPoint newDragLocation = [[self superview] convertPoint:[theEvent locationInWindow] fromView:nil];
    NSPoint thisOrigin = [self frame].origin;
    thisOrigin.x += (-self.lastDragLocation.x + newDragLocation.x);
    thisOrigin.y += (-self.lastDragLocation.y + newDragLocation.y);
    [self setFrameOrigin:thisOrigin];
    self.lastDragLocation = newDragLocation;
}

Кроме того, я бы рекомендовал реорганизовать ваш код, чтобы просто иметь дело с исходным местоположением мыши и текущим местоположением указателя, а не заниматься дельтами между событиями mouseDragged. Это может привести к неожиданным результатам по линии.

Вместо этого просто сохраните смещение между источником перетаскиваемого вида и указателем мыши (где указатель мыши находится внутри представления) и установите начало кадра в положение указателя мыши, за вычетом смещения.

Вот несколько дополнительных показаний:

Cocoa Руководство по рисованию

Cocoa Руководство по обработке событий

Ответ 2

Я думаю, что вы должны рассчитать в соответствии с положением мыши, потому что в соответствии с моим тестом он становится более гладким. Поскольку способ, как показано ниже, обеспечивает только положение внутри системы координат окна приложения:

[[self superview] convertPoint:[theEvent locationInWindow] fromView:nil];

Я предлагаю что-то вроде этого:

lastDrag = [NSEvent mouseLocation];

другие коды одинаковы.