Move a NSWindow by dragging a NSView

7.9k views Asked by At

I have a NSWindow, on which i apply this:

window.styleMask = window.styleMask | NSFullSizeContentViewWindowMask
window.titleVisibility = NSWindowTitleVisibility.Hidden;
window.titlebarAppearsTransparent = true;

I then add a NSView behind the titlebar to simulate a bigger one. Now it looks like this: Current window

I want to be able to move the window, by dragging the light-blue view. I have already tried to subclass NSView and always returning true for mouseDownCanMoveWindow using this code:

class LSViewD: NSView {
    override var mouseDownCanMoveWindow:Bool {
        get {
            return true
        }
    }
}

This didn't work. After some googling i found this INAppStoreWindow on GitHub. However it doesn't support OS X versions over 10.9, so it's completely useless for me.

Edit1

This is how it looks in the Interface Builder. Interface Builder

How can i move the window, by dragging on this NSView?

6

There are 6 answers

0
Leonard Schütz On

I somehow managed to solve my problem, i don't really know how, but here are some screenshots.

enter image description here enter image description here enter image description here

In the AppDelegate file where i edit the properties of my window, i added an IBOutlet of my contentView. This IBOutlet is a subclass of NSView, in which i've overriden the variable mouseDownCanMoveWindow so it always returns false.

I tried this before in only one file, but it didn't work. This however solved the problem.

Thanks to Ken Thomases and Max for leading me into the right direction.

5
mangerlahn On

There are two ways to do this. The first one would be to set the NSTexturedBackgroundWindowMask as well as the windows background color to the one of your view. This should work.

Otherwise you can take a look at this Sample Code

3
Ken Thomases On

Try setting the window's movableByWindowBackground property to true.

0
slashlos On

Swift 3:

I needed this but dynamically. It's a little long but well worth it (IMHO).

So I decided to enable this only while the command key is down. This is achieved by registering a local key handler in the delegate:

// MARK:- Local key monitor
var localKeyDownMonitor : Any? = nil
var commandKeyDown : Bool = false {
    didSet {
        let notif = Notification(name: Notification.Name(rawValue: "commandKeyDown"),
                                 object: NSNumber(booleanLiteral: commandKeyDown))
        NotificationCenter.default.post(notif)
    }
}

func keyDownMonitor(event: NSEvent) -> Bool {
    switch event.modifierFlags.intersection(.deviceIndependentFlagsMask) {

    case [.command]:
        self.commandKeyDown = true
        return true

    default:
        self.commandKeyDown = false
        return false
    }
}

which is enabled within the delegate startup:

func applicationDidFinishLaunching(_ aNotification: Notification) {     
    //  Watch local keys for window movenment, etc.
    localKeyDownMonitor = NSEvent.addLocalMonitorForEvents(matching: NSEventMask.flagsChanged) { (event) -> NSEvent? in
        return self.keyDownMonitor(event: event) ? nil : event
    }
}

and its removal

func applicationWillTerminate(_ aNotification: Notification) {  
    //  Forget key down monitoring
    NSEvent.removeMonitor(localKeyDownMonitor!)
}

Note that when the commandKeyDown value is changed by the key down handler. This value change is caught by the didset{} to post a notification. This notification is registered by any view you wish to have its window so moved - i.e., in the view delegate

override func viewDidLoad() {
    super.viewDidLoad()

    //  Watch command key changes
    NotificationCenter.default.addObserver(
        self,
        selector: #selector(ViewController.commandKeyDown(_:)),
        name: NSNotification.Name(rawValue: "commandKeyDown"),
        object: nil)
}

and discarded when the viewWillDisappear() (delegate) or the window controller windowShouldClose(); add this

    <your-view>.removeObserver(self, forKeyPath: "commandKeyDown")

So sequence goes like this:

  1. key pressed/release
  2. handler called
  3. notification posted

The view's window isMovableByWindowBackground property is changed by notification - placed within view controller / delegate or where you registered the observer.

internal func commandKeyDown(_ notification : Notification) {
    let commandKeyDown : NSNumber = notification.object as! NSNumber
    if let window = self.view.window {
        window.isMovableByWindowBackground = commandKeyDown.boolValue
        Swift.print(String(format: "command %@", commandKeyDown.boolValue ? "v" : "^"))
    }
}

Remove the tracer output when happy. See it in action in SimpleViewer on github.

4
Oskar On

None of the answers here worked for me. They all either don't work at all, or make the whole window draggable (note that OP is not asking for this).

Here's how to actually achieve this:

To make a NSView control the window with it's drag events, simply subclass it and override the mouseDown as such:

class WindowDragView: NSView {

    override public func mouseDown(with event: NSEvent) {
        window?.performDrag(with: event)
    }

}

That's it. The mouseDown function will transfer further event tracking to it's parent window.

No need for window masks, isMovableByWindowBackground or mouseDownCanMoveWindow.

0
Corxit Sun On

Swift3.0 Version

override func viewDidAppear() {

    //for hide the TitleBar
    self.view.window?.styleMask = .borderless
    self.view.window?.titlebarAppearsTransparent = true
    self.view.window?.titleVisibility = .hidden

    //for Window movable with NSView
    self.view.window?.isMovableByWindowBackground = true

}