【问题标题】:Move a NSWindow by dragging a NSView通过拖动 NSView 来移动 NSWindow
【发布时间】:2015-09-06 22:41:18
【问题描述】:

我有一个 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 中的外观。

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

【问题讨论】:

  • 自己解决了,看看我的回答。

标签: macos swift cocoa nsview nswindow


【解决方案1】:

有两种方法可以做到这一点。第一个是将NSTexturedBackgroundWindowMask 以及windows 背景颜色设置为您的视图之一。这应该可以。

要不然你可以看看这个Sample Code

【讨论】:

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

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

【讨论】:

  • 这可行,但它也可以从深蓝色视图中移动窗口,这不是我想要的。它应该只允许从浅蓝色区域移动窗口。
  • 您是否尝试过让深蓝色视图覆盖 mouseDownCanMoveWindow 以返回 false?或者,如果深蓝色视图是不透明的(总是完全填满它的边界),让它的 opaque 属性返回 true?
  • 两个都试过了,没用。要么一切都移动窗口,要么只是标题栏应该在的区域(不是整个浅蓝色栏,看看我在 Interface Builder 中是如何安排的)
【解决方案3】:

我以某种方式设法解决了我的问题,我真的不知道如何,但这里有一些屏幕截图。

在我编辑窗口属性的 AppDelegate 文件中,我添加了我的 contentView 的 IBOutlet。这个 IBOutlet 是 NSView 的子类,我在其中覆盖了变量 mouseDownCanMoveWindow,所以它总是返回 false。

我之前只在一个文件中尝试过,但没有成功。然而,这解决了问题。

感谢Ken ThomasesMax 引导我走向正确的方向。

【讨论】:

    【解决方案4】:

    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
    
    }
    

    【讨论】:

      【解决方案5】:

      斯威夫特 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. 已发布通知

      视图的窗口 isMovableByWindowBackground 属性由通知更改 - 放置在视图控制器/委托或您注册观察者的位置。

      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 中查看它的实际效果。

      【讨论】:

        【解决方案6】:

        这里没有一个答案对我有用。它们要么根本不起作用,要么使整个窗口可拖动(请注意,OP 并没有要求这样做)。

        以下是实际实现的方法:

        要让 NSView 控制带有拖动事件的窗口,只需将其子类化并覆盖 mouseDown,如下所示:

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

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

        不需要窗口遮罩,isMovableByWindowBackgroundmouseDownCanMoveWindow

        【讨论】:

        • 这是最好的答案。不知道为什么覆盖 mouseDownCanMoveWindow 似乎不像记录的那样工作。
        • 不错,谢谢!
        猜你喜欢
        • 2017-12-01
        • 1970-01-01
        • 2016-01-26
        • 1970-01-01
        • 2013-04-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-12-17
        相关资源
        最近更新 更多