我有一个 macOS 应用程序,其中包含 NSTableView 和 NSVisualEffectView。视觉效果视图就像窗口底部的一个栏,它位于表格视图中(包含一些按钮/等...)。
无论如何,如果我想通过拖动视觉效果视图来移动 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
我尝试过的方法都不起作用,如何阻止视觉效果视图将鼠标事件传递到其下面的图层?
最后我设法找到了一个解决方案,值得庆幸的是它不涉及任何库或开源代码(显然也没有私有 api)。
问题
我有一个
NSVisualEffectView
横跨我的视图控制器的宽度,高度为 38 px。它位于我的视图控制器的顶部。它充当包含一些按钮和标签的自定义工具栏。它位于显示各种内容(图像、视频、文本等)的 NSTableView
上方。
我将视觉效果视图放置在表格视图上方,因为我希望当用户滚动表格视图时有一个很好的模糊效果。这样做的问题是,视觉效果视图上的鼠标按下事件会传递到表格视图,而不是整体NSWindow
。这导致用户在单击并拖动视觉效果视图时无法拖动和移动
NSWindow
(因为鼠标按下事件不会传递到窗口)。我注意到视觉效果的顶部 10px DID
将鼠标按下事件传递到窗口而不是表格视图。这是因为窗口的标题栏大约 10-15 像素高。然而我的视觉效果视图的高度是 38px,所以我的视觉效果视图的下半部分无法移动窗口。
解决方案解决方案涉及创建两个子类,一个用于视觉效果视图,另一个用于
NSWindow
。视觉效果视图的子类,只需将鼠标按下事件传递给
nextResponder
(可以是表格视图或窗口 - 取决于窗口标题栏的大小)。标题代码(视觉效果视图类):
#import <Cocoa/Cocoa.h>
@interface TopMainBar : NSVisualEffectView {
}
@end
实现代码(视觉效果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 px,这正是我所需要的)。理想的解决方案是能够将标题栏高度增加到任何自定义尺寸,但这是不可能的,因此工具栏解决方案是唯一的方法。
由于标题栏的大小增加,所有鼠标按下事件都不会传递到窗口而不是表格视图。这使用户能够从视觉效果视图的任何部分拖动窗口。
头代码(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
对象拖放到窗口中来完成此操作。
结论
此解决方案涉及通过将标题栏更改为工具栏来增加标题栏的大小。然后,从视觉效果视图类传递的鼠标按下事件由窗口(而不是其后面的任何其他视图)读取,因此可以移动窗口。