通过拖动NSView来移动NSWindow

12

我有一个NSWindow,在它上面应用了这个:

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

我在标题栏后添加了一个NSView来模拟更大的标题栏。 现在看起来是这样的: 当前窗口

我希望能够通过拖动浅蓝色视图来移动窗口。我已经尝试过子类化NSView并始终使用此代码返回mouseDownCanMoveWindow的true:

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

这个方法不起作用。在搜索了一些信息之后,我在 GitHub 上找到了INAppStoreWindow。然而,它不支持 10.9 以上的 OS X 版本,对我来说完全没有用。

编辑1

在 Interface Builder 中是这样的: Interface Builder

如何通过拖动这个 NSView 移动窗口?


我自己解决了,看看我的答案。 - Leonard Schütz
6个回答

25

这里的所有答案都对我不起作用。它们要么根本不能工作,要么让整个窗口可拖动(请注意,原帖作者并没有要求这样做)。

以下是实现此目的的方法:

要使NSView控制窗口与其拖动事件,只需子类化它并覆盖mouseDown方法:

class WindowDragView: NSView {

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

}

就是这样。 mouseDown函数将进一步的事件跟踪转移到其父窗口。

不需要窗口遮罩,isMovableByWindowBackgroundmouseDownCanMoveWindow


1
这是最好的答案。不确定为什么覆盖 mouseDownCanMoveWindow 看起来没有按照文档工作。 - Nate
不错,谢谢! - Ryan Francesconi

17
尝试将窗口的 movableByWindowBackground 属性设置为 true。

这个方法可以实现,但它也允许从深蓝色视图移动窗口,这不是我想要的。它应该只允许从浅蓝色区域移动窗口。 - Leonard Schütz
1
你尝试过使用深蓝色视图覆盖mouseDownCanMoveWindow并返回false吗?或者,如果深蓝色视图是不透明的(始终完全填充其边界),则将其opaque属性设置为true呢? - Ken Thomases
尝试了两种方法,都没有成功。要么所有的东西都会移动窗口,要么只有标题栏应该在的区域移动(而不是整个浅蓝色的条,看看我在界面构建器中是如何排列的)。 - Leonard Schütz

2
有两种方法可以做到这一点。第一种方法是将NSTexturedBackgroundWindowMask设置为与视图相同的窗口背景颜色。这应该可以实现。
否则,您可以查看此示例代码

请问您能否详细说明应该如何做以及它的作用是什么? - Leonard Schütz
NSTexturedBackgroundWindowMask应该设置在window.stylemask属性上还是其他地方? - Leonard Schütz
没错!之后你把 window.backgroundColor 设为你的蓝色。那么整个窗口都将是蓝色的,所以你需要一个额外的有颜色的视图作为内容视图。 - mangerlahn
这使我能够删除标题栏后面的自定义视图,但并没有解决我的问题。窗口仍然只能在标题栏框架内拖动。如果我启用.movableByWindowBackground,则内容视图也可以移动窗口。我尝试通过覆盖mouseDownCanMoveWindow和opaque属性来关闭它,但没有成功。 - Leonard Schütz
这很有趣,因为它对我有效。然后尝试使用您自定义的视图使用Apple示例代码。他们在视图中跟踪mouseDown,并相应地移动窗口。 - mangerlahn

0

Swift 3:

我需要动态设置。这可能有点长,但值得(在我看来)。

因此,我决定仅在按下命令键时启用它。这是通过在委托中注册本地键处理程序来实现的:

// 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
    }
}

这是在委托启动时启用的:

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
    }
}

以及它的移除

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

注意,当键盘按下事件处理程序改变commandKeyDown值时。这个值的改变被didset{}捕获以发布通知。此通知由您希望其窗口移动的任何视图注册 - 即在视图代理中。
override func viewDidLoad() {
    super.viewDidLoad()

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

并在viewWillDisappear()(委托)或窗口控制器windowShouldClose()时丢弃;添加此内容

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

所以顺序如下:

  1. 按键按下/释放
  2. 处理程序被调用
  3. 通知被发布

视图的window属性是否可通过窗口背景移动由通知更改 - 该通知位于视图控制器/代理或您注册观察者的位置。

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" : "^"))
    }
}

当你满意时,请删除跟踪器输出。在 GitHub 上的 SimpleViewer 中可以看到它的实际效果。


0

Swift3.0 版本

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

}

0

我不知道怎么做的,但是我设法解决了我的问题,这里有一些截图。

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

在AppDelegate文件中编辑窗口属性的位置,我添加了一个contentView的IBOutlet。这个IBOutlet是一个NSView子类,在其中我覆盖了变量mouseDownCanMoveWindow,所以它总是返回false。
我之前曾尝试只在一个文件中这样做,但并没有成功。然而,这种方法解决了问题。
感谢Ken ThomasesMax指引我找到了正确的方向。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接