在之前的博文中我们实现了类似Reminder的实现:《自定义Mac标题栏样式》

然而,使用以上方法实现自定义标题栏,会带来一个额外的副作用:双击Title bar无法再实现默认Window的Zoom效果,即无法自动缩放窗口到全屏效果。

发现这个问题后我测试了Mac自带的Reminder应用,发现同样具有这个问题,由此可见这也许是一个OS X的 Bug,或是一个人所未知的“feature”。Anyway, 为了解决这个问题,我使用了一个比较非主流的方式,记录如下。

一、总体思路

通过查看文档和Storyboard文件,作为First Responder的Window具有一个叫做performZoom的方法,而通过触发这个方法,Window就会执行缩放操作。
如此一来,问题的解决方案可以归为:

  1. 响应双击事件
  2. 判定点击区域是否在title bar内
  3. 执行performZoom进行缩放

二、具体实现

1. 响应双击事件

通过覆写mouseDown(with event: NSEvent)方法,检测鼠标点击事件,实现代码如下:


override func mouseDown(with event: NSEvent) {
        if (event.clickCount > 1) {
            //双击相关处理
    } 
    else{
        super.mouseDown(with: event)
    }
}

2. 判定点击区域是否在title bar内

通过event检测鼠标点击的位置,并进一步通过func convert(_ point: NSPoint, to view: NSView?) -> NSPoint方法将点击点映射到目的View上,从而检测是否点击到了所需的View上。代码如下:


let location = self.view.convert(event.locationInWindow, to: statusBar)
        if (location.x >= 0 && location.y >= 0) {//即点击点位于statusBar内部
            //执行操作
        }

3. 执行performZoom进行缩放


self.view.window?.performZoom(nil)

完整代码如下:


override func mouseDown(with event: NSEvent) {
        if (event.clickCount > 1) {
            //双击相关处理
            let location = self.view.convert(event.locationInWindow, to: statusBar)
            if (location.x >= 0 && location.y >= 0) {//即点击点位于statusBar内部
                self.view.window?.performZoom(nil)
            }
        }
            
        else{
            super.mouseDown(with: event)
        }
    }

三、相关思考

其实一开始使用这样的方法我是拒绝的,因为怎么看都有一种 钦定 黑魔法的感觉。然而在有了比较多的Mac开发经历之后,我对此已经见怪不怪了。相比iOS, Mac开发总是需要subclass更多,hack更多。

除此之外,对知名应用Omnifocus的观察也让我觉得,这很可能是一条可行的路线,通过观察Omnifocus的行为,我发现其相应双击Zoom的响应区域更大,他的整个Tool Bar 和 Title Bar都可以响应双击事件,而根据我的解决方案,只需要将双击判定区域扩大到整个Tool Bar,即可轻松实现这个效果。再对比官方Reminders完全无法Zoom的行为,依然觉得Apple还得多加努力啊。