In the end I managed to find out a solution and thankfully it involves NO libraries or open source code (and obviously no private apis).
The problem
I have a NSVisualEffectView
that spans the width of my view controller and it 38 px tall. It is positioned at the top of my view controller. It acts as a custom toolbar that contains a few buttons and labels. It is placed above a NSTableView
that displays all sorts of content (images, video, text, etc...).
I placed the visual effect view above the table view, because I wanted to have a nice blur effect when the user scrolled the table view. The problem with this, is that the mouse down events on the visual effect view, get passed to table view and NOT the overall NSWindow
. This results in the user being unable to drag and move the NSWindow
, when they click and drag the visual effect view (because the mouse down events are not passed to the window).
I noticed that the top 10px of the visual effect DID pass the mouse down events to the window and not the table view. This is because the window's title bar is around 10-15px tall. However my visual effect view is 38px tall, so the bottom half of my visual effect view was unable to move the window.
The solution
The solution involves making two subclasses, one for the visual effect view and another for the NSWindow
. The subclass for the visual effect view, simply passes the mouse down events to the nextResponder
(which can be the table view or the window - depending on the size of the window title bar).
Header code (Visual Effect View class):
#import <Cocoa/Cocoa.h>
@interface TopMainBar : NSVisualEffectView {
}
@end
Implementation code (Visual Effect View 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
The subclass for the window involves turning the window title bar into a toolbar, this in effect increases the size of the title bar (and as it happens increases it to around 38 px which is exactly what I needed). The ideal solution, would involve being able to increase the title bar height to any custom size, however that is not possible, so the toolbar solution is the only way.
Because the size of the title bar is increased, all the mouse down events are not passed to the window and not the table view. This enables the user to drag the window from any part of the visual effect view.
Header code (Window class):
#import <Cocoa/Cocoa.h>
@interface CustomWindow : NSWindowController <NSWindowDelegate> {
}
// UI methods.
-(BOOL)isWindowFullScreen;
@end
Implementation code (Window class):
#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
In the custom window class you can see that I am changing the toolbar visibility depending on the full screen state of the window. This is to stop title bar appearing and covering my custom visual effect view up, when the window goes into full screen mode.
In order for this to work, you need to add an empty toolbar to your window, you can do this in interface builder, by dragging and dropping a NSToolbar
object, to your window.
Make sure you connect the window to the window delegate, otherwise the full screen delegate method will not be called.
Conclusion
This solution involves increasing the size of the title bar by changing it into a toolbar. The mouse down events that are passed from the visual effect view class, are then read by the window (not any other view behind it) and thus the window can be moved.
-drawRect:
. – Ssswift