通过拖动高于其他视图的NSView来移动NSWindow

时间:2017-06-28 09:57:27

标签: objective-c macos cocoa nsview nswindow

我有一个包含NSTableView和NSVisualEffectView的macOS应用程序。视觉效果视图就像窗口底部的条形图一样,它位于表格视图中(包含几个按钮/等等。)。

无论如何,如果我想通过拖动视觉效果视图来移动NSWindow,它只有在表视图不在视觉效果视图下方时才有效。我希望视觉效果视图位于表格视图上方的原因是,当用户滚动浏览表格视图内容时,我得到了很好的模糊效果。

但是,当视觉效果视图位于表格视图上方时,不会注册鼠标/拖动/等事件。相反,它们被传递到表视图。我怎样才能阻止这种情况发生?

我尝试了继承NSVisualEffectView,但我尝试过的所有内容都失败了。这是我的代码:

#import <Cocoa/Cocoa.h>

@interface BottomMainBar : NSVisualEffectView {

}

@end

以下是实施代码:

#import "BottomMainBar.h"

@implementation BottomMainBar

/// DRAW RECT METHOD ///

-(void)drawRect:(NSRect)dirtyRect {
    [super drawRect:dirtyRect];

    [self setWantsLayer:YES];

    [self.window setMovableByWindowBackground:YES];

    [self setAcceptsTouchEvents:YES];

    [self registeredDraggedTypes];
}

/// OTHER METHODS ///

-(BOOL)mouseDownCanMoveWindow {
    return YES;
}

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

-(void)mouseDown:(NSEvent *)event {}
-(void)mouseDragged:(NSEvent *)event {}
-(void)mouseUp:(NSEvent *)event {}
-(void)mouseEntered:(NSEvent *)event {}
-(void)mouseExited:(NSEvent *)event {}

@end

我没有尝试过任何工作,如何阻止视觉效果视图将鼠标事件传递给它下面的图层?

谢谢你的时间,Dan。

1 个答案:

答案 0 :(得分:0)

最后我设法找到了解决方案,幸好它涉及没有库或开源代码(显然没有私有api)。

问题

我有一个NSVisualEffectView,它跨越了我的视图控制器的宽度,高度为38像素。它位于我的视图控制器的顶部。它充当自定义工具栏,其中包含一些按钮和标签。它位于NSTableView上方,可显示各种内容(图像,视频,文字等)。

我将视觉效果视图放在表格视图上方,因为当用户滚动表格视图时,我希望有一个很好的模糊效果。这个问题是,视觉效果视图上的鼠标按下事件,传递到表格视图, NOT 整体NSWindow。这导致用户在单击并拖动视觉效果视图时无法拖动和移动NSWindow(因为鼠标按下事件未传递到窗口)。

我注意到视觉效果的前10px DID 将鼠标按下事件传递给窗口而不是表视图。这是因为窗口的标题栏高约10-15px。但是我的视觉效果视图是38px高,因此我的视觉效果视图的下半部分无法移动窗口。

解决方案

解决方案涉及制作两个子类,一个用于视觉效果视图,另一个用于NSWindow。视觉效果视图的子类,只需将鼠标按下事件传递给nextResponder(可以是表视图或窗口 - 取决于窗口标题栏的大小)。

标题代码(Visual Effect View类):

#import <Cocoa/Cocoa.h>

@interface TopMainBar : NSVisualEffectView {

}

@end

实现代码(Visual Effect View类):

#import "TopMainBar.h"

@implementation TopMainBar

/// INIT WITH FRAME ///

-(id)initWithFrame:(NSRect)frameRect {

    if ((self = [super initWithFrame:frameRect])) {
        [self setWantsLayer:YES];
        [self.window setMovableByWindowBackground:YES];
    }

    return self;
}

/// MOUSE METHODS ///

-(void)mouseDown:(NSEvent *)event {
    [self.window mouseDown:event];
}

@end

窗口的子类涉及将窗口标题栏转换为工具栏,这实际上增加了标题栏的大小(并且当它发生时将其增加到大约38像素,这正是我需要的)。理想的解决方案是能够将标题栏高度增加到任何自定义大小,但这是不可能的,因此工具栏解决方案是唯一的方法。

由于标题栏的大小增加,所有鼠标按下事件都不会传递到窗口而不是表视图。这使用户可以从视觉效果视图的任何部分拖动窗口。

标题代码(Window类):

#import <Cocoa/Cocoa.h>

@interface CustomWindow : NSWindowController <NSWindowDelegate> {

}

// UI methods.
-(BOOL)isWindowFullScreen;

@end

实现代码(Window类):

#import "CustomWindow.h"

@interface CustomWindow ()

@end

@implementation CustomWindow

/// WINDOW DID LOAD ///

-(void)windowDidLoad {
    [super windowDidLoad];

    // Ensure this window is the current selected one.
    [self.window makeKeyAndOrderFront:self];

    // Ensure the window can be moved.
    [self.window setMovableByWindowBackground:YES];

    // Set the window title bar options.
    self.window.titleVisibility = NSWindowTitleHidden;
    self.window.titlebarAppearsTransparent = YES;
    self.window.styleMask |= (NSWindowStyleMaskFullSizeContentView | NSWindowStyleMaskUnifiedTitleAndToolbar | NSWindowStyleMaskTitled);
    self.window.movableByWindowBackground = YES;
    self.window.toolbar.showsBaselineSeparator = NO;
    self.window.toolbar.fullScreenAccessoryView.hidden = YES;
    self.window.toolbar.visible = ![self isWindowFullScreen];
}

/// UI METHODS ///

-(BOOL)isWindowFullScreen {
    return (([self.window styleMask] & NSWindowStyleMaskFullScreen) == NSWindowStyleMaskFullScreen);
}

/// WINDOW METHODS ///

-(void)windowWillEnterFullScreen:(NSNotification *)notification {
    self.window.toolbar.visible = NO;
}

-(void)windowDidEnterFullScreen:(NSNotification *)notification {
    self.window.toolbar.visible = NO;
}

-(void)windowWillExitFullScreen:(NSNotification *)notification {
    self.window.toolbar.visible = YES;
}

-(void)windowDidExitFullScreen:(NSNotification *)notification {
    self.window.toolbar.visible = YES;
}

/// OTHER METHODS ///

-(BOOL)mouseDownCanMoveWindow {
    return YES;
}

@end

在自定义窗口类中,您可以看到我正在根据窗口的全屏状态更改工具栏可见性。这是为了在窗口进入全屏模式时停止显示标题栏并覆盖我的自定义视觉效果视图。

为了使其工作,您需要在窗口中添加一个空工具栏,您可以在界面构建器中通过将NSToolbar对象拖放到窗口来执行此操作。

enter image description here

确保将窗口连接到窗口委托,否则将不会调用全屏委托方法。

<强>结论

此解决方案涉及通过将标题栏更改为工具栏来增加标题栏的大小。然后,窗口(而不是其后面的任何其他视图)读取从视觉效果视图类传递的鼠标按下事件,从而可以移动窗口。