在開發落格輸入法 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、選擇、drawInterior和draw 方法,但是我發現edit、drawInterior和draw並沒有被調用,只有select方法在編輯狀態下會被調用,現在的結果就是,只有在編輯狀態的時候,textfield會垂直居中,退出編輯狀態後,textfield會恢復垂直居上,我該怎麼辦
很遺憾這我可能也幫不了你啥,我這個辦法,最終也無法對所有字體居中,最後我的辦法是給用戶自定義高度偏移量……