macOS 开发之 NSTextField 文本的垂直居中

在 macOS 开发中不知道您是否为 NSTextField 的垂直居中的问题困扰过呢!从今天这篇文章开始,这个问题将不再是问题,本文将介绍一种可以实现 NSTextField 垂直居中的方法,大概可以满足我们的需求!

开发平台

  • macOS 10.14.3
  • swift 4.2.1
  • xcode 10.1

准备 Demo

还是老规矩,以实例出发,理解实现!新建一个 macOS app 的工程,使用 storyboard,这里工程就命名为 TextFieldDemo 吧。创建成功后,打开 Main.storyboard ,往 View Controller 中添加一个 Text Field ,调整其大小贴齐窗口四边,添加必要的布局限制,并将以下内容作为 Text Field 的内容:

漫威电影宇宙(英语:Marvel Cinematic Universe,简称MCU)是由漫威漫画工作室基于漫威漫画出版物中的角色独立制作的一系列电影所构成的架空世界和共同世界(Earth-199999)。

就像下面这样(十里这里将窗口调小了一点):

运行程序可以看到显示文本是默认向上对齐的:

实现思路

最终我们要实现 Demo 窗口中的文本垂直居中显示,要想做到这一点,我们得了解一下文本框显示文本的过程。NSTextField 类通过 NSTextFieldCell 类实现用户界面的显示,所以关注点应该放在 NSTextFieldCell 类上,文本是在 text field cell 上的,而 TextField 又是 text field cell 的一个容器,所以只需要调整 text field cell 在 Text Field 中的坐标,如果 y 轴坐标值合适就能实现居中,在 Cocoa 中,控件使用如下坐标系(flipped coordinate):

NSTextFieldCell 类继承于 NSCell 类,所以可以通过调整绘制过程中 text field cell 的 frame 坐标数据实现其位置调整,对应这个绘制过程的方法就是 drawingRect(forBounds:),新坐标的计算公式为:

$$ y_{new} = y_{old} + (Height_{frame} - Height_{text}) \times 0.5 $$

其中:

  • $Height_{frame}$ 是指默认生成的 cell 的 frame
  • $Height_{text}$ 是文本实际占用的 frame

为了更彻底一点,我们也可以调整 frame 的高度与文本实际占用的高度一致:

$$ Height_{frame} = Height_{text} $$

也许您看了下面的图就明白上面公式的含义了:

具体实现

根据上面的思路可以有两种实现方式,一种是定义一个 NSTextFielCell 的子类,在子类中重写方法,另一种是扩展的方式重写 NSTextFieldCell 的方法。第二种会影响 app 中所有的 TextField,所以这里采用第一种方式。

AppDelegate.swift 文件中定义子类 VerticallyCenteredTextFieldCell 如下:

class VerticallyCenteredTextFieldCell: NSTextFieldCell {
    
    override func drawingRect(forBounds theRect: NSRect) -> NSRect {
        var newRect:NSRect = super.drawingRect(forBounds: theRect)
        let textSize:NSSize = self.cellSize(forBounds: theRect)
        let heightDelta:CGFloat = newRect.size.height - textSize.height
        if heightDelta > 0 {
            newRect.size.height = textSize.height
            newRect.origin.y += heightDelta / 2
        }
        return newRect
    }
}

然后打开 Main.storyboard 文件,选中之前添加的 Text Field 的 cell ,按快捷键 Option + Command + 3 打开 Identity 检查器,在 Custom Class 中为 cell 指定刚定义的子类 VerticallyCenteredTextFieldCell:

运行程序就能看到文本实现了居中显示:

Demo下载:VerticallyCenteredTextFieldDemo.zip