可搜索,可注册,可登录,致敬逗比大佬!尽在救援版逗比根据地 dbgjd.com
投稿文章 | 广告合作 | Telegram 群组 / 公告频道 / 使用教程

让 iOS macOS 中文字体实现视觉垂直居中

News 转载自https://www.logcg.com 65℃ 0评论
最近更新:18th 三月, 2019

在开发落格输入法 macOS 的时候,我遇到了一个比较奇葩的问题,这个问题一直困扰我到现在——当有些地方需要垂直居中显示一排文字的时候,如何让这些字真正的“居中”?

 

乍看之下这似乎没什么道理,垂直居中嘛……等等,macOS 上的 NSTextField  还真没有办法让你的一行文字垂直居中……🤷‍♂️

第一代方案

后来,我参考网上的一些解决方案,自己动手写了一个 NSTextFieldCell  的子类,这样文本就能真正地实现垂直居中了,代码如下:

Swift

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
final class VerticallyCenteredTextFieldCell: NSTextFieldCell {
    func adjustedFrame(toVerticallyCenterText rect: NSRect) -> NSRect {
        // super would normally draw text at the top of the cell
        var titleRect = super.titleRect(forBounds: rect)
        
        let minimumHeight = self.cellSize(forBounds: rect).height
        titleRect.origin.y += (titleRect.height minimumHeight) / 2
        titleRect.size.height = minimumHeight
        
        return titleRect
    }
    
    override func edit(withFrame rect: NSRect, in controlView: NSView, editor textObj: NSText, delegate: Any?, event: NSEvent?) {
        super.edit(withFrame: adjustedFrame(toVerticallyCenterText: rect), in: controlView, editor: textObj, delegate: delegate, event: event)
    }
    
    override func select(withFrame rect: NSRect, in controlView: NSView, editor textObj: NSText, delegate: Any?, start selStart: Int, length selLength: Int) {
        super.select(withFrame: adjustedFrame(toVerticallyCenterText: rect), in: controlView, editor: textObj, delegate: delegate, start: selStart, length: selLength)
    }
    
    override func drawInterior(withFrame cellFrame: NSRect, in controlView: NSView) {
        super.drawInterior(withFrame: adjustedFrame(toVerticallyCenterText: cellFrame), in: controlView)
    }
    
    override func draw(withFrame cellFrame: NSRect, in controlView: NSView) {
        super.draw(withFrame: cellFrame, in: controlView)
    }
}

 

第二代方案

后来为了提升布局效率,我不再使用 NSTextField ,而是直接在 NSView  上绘制文本,不过原理还是一样的,根据字体视觉居中来设置 y 值偏移量。

但是好景不长,很快,落格输入法就要支持候选栏自定义任意字体了,这下就很糟糕了——因为并不是所有的字体都有着同样的高度,比如同是 18 号大小的中文字,系统字体和系统自带娃娃体字体高度就是不同的:

让 iOS macOS 中文字体实现视觉垂直居中
相比与系统自带,娃娃体明显靠下

你看,同样大小的字号,右侧娃娃体明显比左侧系统自带字体要矮一些——关键是变矮后,他们都是基于字体基准线来排布的,导致娃娃体这个字体在实际视觉中“靠下”了。

事实上类似娃娃体这样的字体有很多,比如宋体、楷体等中文字体几乎全都是不尽相同的低矮设计,这样你就很难找到一个通用的偏移量去垂直居中文字。

第三代方案

这次,既然已经直接绘制文本了,那么我就从字体属性本身下手,查看字体声明,我发现了一个有趣的属性 boundingRectForFont ,这个属性返回一个表示文字字符边界的矩形,这就很有意思了,不同的字,字符的高度不同,边界的高度也就不一样。

所以我在每次布局时都创建一个相同字号的系统字体,获取字体边界,再获取用户自定义的字体的边界,两者取高度差,就是用户字体和系统自带字体的字符高度差了。然后,再根据上文中的思路,调整对应偏移量,即可得到真正的视觉垂直居中布局:

让 iOS macOS 中文字体实现视觉垂直居中
动态计算偏移量后实现任意字体视觉上垂直居中

 

另,有些老字体比如 宋体 就很尴尬,它在同样大小字号下比系统默认字体的矩形还要大,但实际字体大小与系统字体相同,同时字体整体是“底部对齐”,于是差值是负数,结果导致这个字体本身就靠下了,偏移后反而更靠下了……对于这类,我大力出奇迹,直接取绝对值就好了……🤷‍♂️

 

 

 

转载请注明:逗比根据地 » 让 iOS macOS 中文字体实现视觉垂直居中

喜欢 (0)
发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址