在开发落格输入法 macOS 的时候,我遇到了一个比较奇葩的问题,这个问题一直困扰我到现在——当有些地方需要垂直居中显示一排文字的时候,如何让这些字真正的“居中”?
乍看之下这似乎没什么道理,垂直居中嘛……等等,macOS 上的 NSTextField 还真没有办法让你的一行文字垂直居中……🤷♂️
第一代方案
后来,我参考网上的一些解决方案,自己动手写了一个 NSTextFieldCell 的子类,这样文本就能真正地实现垂直居中了,代码如下:
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 号大小的中文字,系统字体和系统自带娃娃体字体高度就是不同的:
你看,同样大小的字号,右侧娃娃体明显比左侧系统自带字体要矮一些——关键是变矮后,他们都是基于字体基准线来排布的,导致娃娃体这个字体在实际视觉中“靠下”了。
事实上类似娃娃体这样的字体有很多,比如宋体、楷体等中文字体几乎全都是不尽相同的低矮设计,这样你就很难找到一个通用的偏移量去垂直居中文字。
第三代方案
这次,既然已经直接绘制文本了,那么我就从字体属性本身下手,查看字体声明,我发现了一个有趣的属性 boundingRectForFont ,这个属性返回一个表示文字字符边界的矩形,这就很有意思了,不同的字,字符的高度不同,边界的高度也就不一样。
所以我在每次布局时都创建一个相同字号的系统字体,获取字体边界,再获取用户自定义的字体的边界,两者取高度差,就是用户字体和系统自带字体的字符高度差了。然后,再根据上文中的思路,调整对应偏移量,即可得到真正的视觉垂直居中布局:
另,有些老字体比如 宋体 就很尴尬,它在同样大小字号下比系统默认字体的矩形还要大,但实际字体大小与系统字体相同,同时字体整体是“底部对齐”,于是差值是负数,结果导致这个字体本身就靠下了,偏移后反而更靠下了……对于这类,我大力出奇迹,直接取绝对值就好了……🤷♂️
本文由 落格博客 原创撰写:落格博客 » 让 iOS macOS 中文字体实现视觉垂直居中
转载请保留出处和原文链接:https://www.logcg.com/archives/3186.html
老哥,我也在搞垂直居中,但是没有达到预想效果,我时使用分类重写的edit、select、drawInterior和draw 方法,但是我发现edit、drawInterior和draw并没有被调用,只有select方法在编辑状态下会被调用,现在的结果就是,只有在编辑状态的时候,textfield会垂直居中,退出编辑状态后,textfield会恢复垂直居上,我该怎么办
很遗憾这我可能也帮不了你啥,我这个办法,最终也无法对所有字体居中,最后我的办法是给用户自定义高度偏移量……