macos 允许单击并拖动视图来拖动窗口本身吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4563893/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-21 07:30:47  来源:igfitidea点击:

Allow click and dragging a view to drag the window itself?

objective-cmacoscocoanswindowappkit

提问by d11wtq

I'm using a textured window that has a tab bar along the top of it, just below the title bar.

我正在使用一个带纹理的窗口,它的顶部有一个标签栏,就在标题栏的下方。

I've used -setContentBorderThickness:forEdge:on the window to make the gradient look right, and to make sure sheets slide out from the right position.

-setContentBorderThickness:forEdge:在窗口上使用过使渐变看起来正确,并确保纸张从正确的位置滑出。

What's not working however, is dragging the window around. It works if I click and drag the area that's actually the title bar, but since the title bar gradient spills into a (potentially/often empty) tab bar, it's reallyeasy to click too low and it feels really frustrating when you try to drag and realise the window is not moving.

然而,不起作用的是拖动窗口。如果我单击并拖动实际上是标题栏的区域,它会起作用,但是由于标题栏渐变溢出到(可能/通常是空的)标签栏,点击太低真的很容易,当你尝试拖动时感觉真的很令人沮丧并意识到窗口没有移动。

I notice NSToolbar, while occupying roughly the same amount of space below the title bar, allows the window to be dragged around when the cursor is over it. How does one implement this?

我注意到NSToolbar,虽然在标题栏下方占据大致相同的空间,但允许在光标悬停在其上方时拖动窗口。如何实现这一点?

Thanks.

谢谢。

Tab Bar

标签栏

采纳答案by d11wtq

I found this here:

我在这里找到了这个:

-(void)mouseDown:(NSEvent *)theEvent {    
    NSRect  windowFrame = [[self window] frame];

    initialLocation = [NSEvent mouseLocation];

    initialLocation.x -= windowFrame.origin.x;
    initialLocation.y -= windowFrame.origin.y;
}

- (void)mouseDragged:(NSEvent *)theEvent {
    NSPoint currentLocation;
    NSPoint newOrigin;

    NSRect  screenFrame = [[NSScreen mainScreen] frame];
    NSRect  windowFrame = [self frame];

    currentLocation = [NSEvent mouseLocation];
    newOrigin.x = currentLocation.x - initialLocation.x;
    newOrigin.y = currentLocation.y - initialLocation.y;

    // Don't let window get dragged up under the menu bar
    if( (newOrigin.y+windowFrame.size.height) > (screenFrame.origin.y+screenFrame.size.height) ){
        newOrigin.y=screenFrame.origin.y + (screenFrame.size.height-windowFrame.size.height);
    }

    //go ahead and move the window to the new location
    [[self window] setFrameOrigin:newOrigin];
}

It works fine, though I'm not 100% sure I'm doing it correctly. There's one bug I've found so far, and that's if the drag begins inside a subview (a tab itself) and then enters the superview (the tab bar). The window jumps around. Some -hitTest: magic, or possibly even just invalidating initialLocation on mouseUp should probably fix that.

它工作正常,尽管我不是 100% 确定我做对了。到目前为止,我发现了一个错误,那就是如果拖动从子视图(选项卡本身)内开始,然后进入超级视图(选项卡栏)。窗户跳来跳去。一些 -hitTest: 魔法,或者甚至可能只是使 mouseUp 上的 initialLocation 无效应该可以解决这个问题。

回答by ArtOfWarfare

I tried the mouseDownCanMoveWindowsolution (https://stackoverflow.com/a/4564146/901641) but it didn't work for me. I got rid of that method and instead added this to my window subclass:

我尝试了mouseDownCanMoveWindow解决方案(https://stackoverflow.com/a/4564146/901641),但对我不起作用。我摆脱了该方法,而是将其添加到我的窗口子类中:

- (BOOL)isMovableByWindowBackground {
    return YES;
}

which worked like a charm.

这就像一个魅力。

回答by Richard

Have you tried overriding the NSView method mouseDownCanMoveWindowto return YES?

您是否尝试覆盖 NSView 方法mouseDownCanMoveWindow以返回YES

回答by Nix Wang

It works for me after TWO steps:

它在两个步骤后对我有用:

  1. Subclass NSView, override the mouseDownCanMoveWindowto return YES.
  2. Subclass NSWindow, override the isMovableByWindowBackgroundto return YES.
  1. 子类 NSView,覆盖mouseDownCanMoveWindow返回 YES。
  2. 子类 NSWindow,覆盖isMovableByWindowBackground返回 YES。

回答by Dimitri Bouniol

As of macOS 10.11, the simplest way to do this is to utilize the new -[NSWindow performWindowDragWithEvent:]method:

从 macOS 10.11 开始,最简单的方法是使用新-[NSWindow performWindowDragWithEvent:]方法:

@interface MyView () {
    BOOL movingWindow;
}
@end

@implementation MyView

...

- (BOOL)mouseDownCanMoveWindow
{
    return NO;
}

- (void)mouseDown:(NSEvent *)event
{
    movingWindow = NO;

    CGPoint point = [self convertPoint:event.locationInWindow
                              fromView:nil];

    // The area in your view where you want the window to move:
    CGRect movableRect = CGRectMake(0, 0, 100, 100);

    if (self.window.movableByWindowBackground &&
        CGRectContainsPoint(movableRect, point)) {

        [self.window performWindowDragWithEvent:event];
        movingWindow = YES;
        return;
    }

    // Handle the -mouseDown: as usual
}

- (void)mouseDragged:(NSEvent *)event
{
    if (movingWindow) return;

    // Handle the -mouseDragged: as usual
}

@end

Here, -performWindowDragWithEvent:will handle the correct behavior of not overlapping the menu bar, and will also snap to edges on macOS 10.12 and later. Be sure to include a BOOL movingWindowinstance variable with your view's private interface so you can avoid -mouseDragged:events once you determined you don't want to process them.

在这里,-performWindowDragWithEvent:将处理不重叠菜单栏的正确行为,并且还将在 macOS 10.12 及更高版本上对齐边缘。确保BOOL movingWindow在视图的私有接口中包含一个实例变量,这样-mouseDragged:一旦确定不想处理事件,就可以避免发生事件。

Here, we are also checking that -[NSWindow movableByWindowBackground]is set to YESso that this view can be used in non-movable-by-window-background windows, but that is optional.

在这里,我们还检查-[NSWindow movableByWindowBackground]设置为,YES以便此视图可以在不可移动的窗口背景窗口中使用,但这是可选的。

回答by D.A.H

It's quite easy:

这很容易:

override mouseDownCanMoveWindowproperty

覆盖mouseDownCanMoveWindow属性

override var mouseDownCanMoveWindow:Bool {
    return false
}

回答by Paulo Mattos

If you got a NSTableViewin your window, with selection enabled, overriding the mouseDownCanMoveWindowproperty won't work.

如果您NSTableView的窗口中有 ,并且选择已启用,则覆盖该mouseDownCanMoveWindow属性将不起作用。

You need instead to create a NSTableViewsubclass and override the following mouse events (and use the performWindowDragWithEvent:mentioned in Dimitri answer):

您需要创建一个NSTableView子类并覆盖以下鼠标事件(并使用Dimitri answer 中performWindowDragWithEvent:提到的):

@interface WindowDraggableTableView : NSTableView
@end

@implementation WindowDraggableTableView 
{
    BOOL _draggingWindow;
    NSEvent *_mouseDownEvent;
}

- (void)mouseDown:(NSEvent *)event
{
    if (self.window.movableByWindowBackground == NO) {
        [super mouseDown:event]; // Normal behavior.
        return;
    }

    _draggingWindow = NO;
    _mouseDownEvent = event;
}

- (void)mouseDragged:(NSEvent *)event
{
    if (self.window.movableByWindowBackground == NO) {
        [super mouseDragged:event]; // Normal behavior.
        return;
    }

    assert(_mouseDownEvent);
    _draggingWindow = YES;
    [self.window performWindowDragWithEvent:_mouseDownEvent];
}

- (void)mouseUp:(NSEvent *)event
{
    if (self.window.movableByWindowBackground == NO) {
        [super mouseUp:event]; // Normal behavior.
        return;
    }

    if (_draggingWindow == YES) {
        _draggingWindow = NO;
        return; // Event already handled by `performWindowDragWithEvent`.
    }

    // Triggers regular table selection.
    NSPoint locationInWindow = event.locationInWindow;
    NSPoint locationInTable = [self convertPoint:locationInWindow fromView:nil];
    NSInteger row = [self rowAtPoint:locationInTable];
    if (row >= 0 && [self.delegate tableView:self shouldSelectRow:row])
    {
        NSIndexSet *rowIndex = [NSIndexSet indexSetWithIndex:row];
        [self selectRowIndexes:rowIndex byExtendingSelection:NO];
    }
}

@end

Also don't forget to set the corresponding window movableByWindowBackgroundproperty as well:

另外不要忘记设置相应的窗口movableByWindowBackground属性:

self.window.movableByWindowBackground = YES;

回答by Ely

When you set property isMovableByWindowBackgroundin viewDidLoad, it may not work because the windowproperty of the viewis not yet set. In that case, try this:

当您在 中设置属性isMovableByWindowBackgroundviewDidLoad,它可能不起作用,因为 的window属性view尚未设置。在这种情况下,试试这个:

override func viewDidAppear() {
    self.view.window?.isMovableByWindowBackground = true
}