通过拖动其他视图上方的 NSView 来移动 NSWindow
Move NSWindow by dragging NSView that is above other views
我有一个包含 NSTableView 和 NSVisualEffectView 的 macOS 应用程序。视觉效果视图就像window底部的一个栏,它在table视图中(包含一些buttons/etc..)。
无论如何,如果我想通过拖动视觉效果视图来移动 NSWindow,只有当 table 视图不在视觉效果视图下方时才有效。我希望视觉效果视图位于 table 视图上方的原因是,当用户滚动浏览 table 视图内容时,我可以获得很好的模糊效果。
但是,当视觉效果视图在table视图上方时,mouse/drag/etc事件不会被注册。相反,它们被传递到 table 视图。我怎样才能阻止这种情况发生?
我尝试子类化 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 像素高。它位于我的视图控制器的顶部。它充当包含一些按钮和标签的自定义工具栏。它位于显示各种内容(图像、视频、文本等)的 NSTableView
上方。
我将视觉效果视图放在 table 视图上方,因为我想在用户滚动 table 视图时有一个很好的模糊效果。这个问题是,视觉效果视图上的鼠标按下事件被传递到 table 视图并且 NOT 整体 NSWindow
。这导致用户在单击并拖动视觉效果视图时无法拖动和移动 NSWindow
(因为鼠标按下事件未传递给 window)。
我注意到视觉效果 DID 的前 10px 将鼠标按下事件传递给 window 而不是 table 视图。这是因为 window 的标题栏大约有 10-15 像素高。但是我的视觉效果视图是38px高,所以我的视觉效果视图的下半部分无法移动 window.
解决方法
解决方案涉及制作两个子class,一个用于视觉效果视图,另一个用于NSWindow
。视觉效果视图的 subclass 只是将鼠标按下事件传递给 nextResponder
(可以是 table 视图或 window - 取决于大小window 标题栏)。
Header代码(视觉效果查看class):
#import <Cocoa/Cocoa.h>
@interface TopMainBar : NSVisualEffectView {
}
@end
实现代码(视觉效果查看class):
#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
window 的子class 涉及将 window 标题栏变成工具栏,这实际上增加了标题栏的大小(并且碰巧增加了它到大约 38 像素,这正是我所需要的)。理想的解决方案是能够将标题栏高度增加到任何自定义大小,但这是不可能的,因此工具栏解决方案是唯一的方法。
因为标题栏的大小增加了,所以所有鼠标按下事件都不会传递给 window 而不是 table 视图。这使用户能够从视觉效果视图的任何部分拖动 window。
Header代码(Windowclass):
#import <Cocoa/Cocoa.h>
@interface CustomWindow : NSWindowController <NSWindowDelegate> {
}
// UI methods.
-(BOOL)isWindowFullScreen;
@end
实现代码(Windowclass):
#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
在自定义 window class 中,您可以看到我正在根据 window 的全屏状态更改工具栏可见性。这是为了在 window 进入全屏模式时停止出现标题栏并覆盖我的自定义视觉效果视图。
为了使其正常工作,您需要向 window 添加一个空工具栏,您可以在界面生成器中通过拖放 NSToolbar
object , 到你的 window.
确保将 window 连接到 window 委托,否则将不会调用全屏委托方法。
结论
此解决方案涉及通过将标题栏更改为工具栏来增加标题栏的大小。从视觉效果视图 class 传递的鼠标按下事件随后由 window(不是它后面的任何其他视图)读取,因此 window 可以移动。
我有一个包含 NSTableView 和 NSVisualEffectView 的 macOS 应用程序。视觉效果视图就像window底部的一个栏,它在table视图中(包含一些buttons/etc..)。
无论如何,如果我想通过拖动视觉效果视图来移动 NSWindow,只有当 table 视图不在视觉效果视图下方时才有效。我希望视觉效果视图位于 table 视图上方的原因是,当用户滚动浏览 table 视图内容时,我可以获得很好的模糊效果。
但是,当视觉效果视图在table视图上方时,mouse/drag/etc事件不会被注册。相反,它们被传递到 table 视图。我怎样才能阻止这种情况发生?
我尝试子类化 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 像素高。它位于我的视图控制器的顶部。它充当包含一些按钮和标签的自定义工具栏。它位于显示各种内容(图像、视频、文本等)的 NSTableView
上方。
我将视觉效果视图放在 table 视图上方,因为我想在用户滚动 table 视图时有一个很好的模糊效果。这个问题是,视觉效果视图上的鼠标按下事件被传递到 table 视图并且 NOT 整体 NSWindow
。这导致用户在单击并拖动视觉效果视图时无法拖动和移动 NSWindow
(因为鼠标按下事件未传递给 window)。
我注意到视觉效果 DID 的前 10px 将鼠标按下事件传递给 window 而不是 table 视图。这是因为 window 的标题栏大约有 10-15 像素高。但是我的视觉效果视图是38px高,所以我的视觉效果视图的下半部分无法移动 window.
解决方法
解决方案涉及制作两个子class,一个用于视觉效果视图,另一个用于NSWindow
。视觉效果视图的 subclass 只是将鼠标按下事件传递给 nextResponder
(可以是 table 视图或 window - 取决于大小window 标题栏)。
Header代码(视觉效果查看class):
#import <Cocoa/Cocoa.h>
@interface TopMainBar : NSVisualEffectView {
}
@end
实现代码(视觉效果查看class):
#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
window 的子class 涉及将 window 标题栏变成工具栏,这实际上增加了标题栏的大小(并且碰巧增加了它到大约 38 像素,这正是我所需要的)。理想的解决方案是能够将标题栏高度增加到任何自定义大小,但这是不可能的,因此工具栏解决方案是唯一的方法。
因为标题栏的大小增加了,所以所有鼠标按下事件都不会传递给 window 而不是 table 视图。这使用户能够从视觉效果视图的任何部分拖动 window。
Header代码(Windowclass):
#import <Cocoa/Cocoa.h>
@interface CustomWindow : NSWindowController <NSWindowDelegate> {
}
// UI methods.
-(BOOL)isWindowFullScreen;
@end
实现代码(Windowclass):
#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
在自定义 window class 中,您可以看到我正在根据 window 的全屏状态更改工具栏可见性。这是为了在 window 进入全屏模式时停止出现标题栏并覆盖我的自定义视觉效果视图。
为了使其正常工作,您需要向 window 添加一个空工具栏,您可以在界面生成器中通过拖放 NSToolbar
object , 到你的 window.
确保将 window 连接到 window 委托,否则将不会调用全屏委托方法。
结论
此解决方案涉及通过将标题栏更改为工具栏来增加标题栏的大小。从视觉效果视图 class 传递的鼠标按下事件随后由 window(不是它后面的任何其他视图)读取,因此 window 可以移动。