如何阻止NSTextView在指定字体不支持字符时使用级联字体回退
阻止NSTextView自动字体回退,显示缺失字符占位符
这个需求我之前做文本编辑器的时候刚好碰到过,NSTextView默认的字体回退确实会干扰我们判断字符是否在指定字体中可用。给你两个靠谱的解决思路:
方案一:通过NSLayoutManagerDelegate拦截字形生成
这是最精准的方式,直接在NSTextView渲染字形的过程中拦截,替换掉那些当前字体不支持的字符对应的字形。
首先,让你的视图控制器或者自定义NSTextView类遵守NSLayoutManagerDelegate协议,然后实现下面这个方法:
func layoutManager(_ layoutManager: NSLayoutManager, shouldGenerateGlyphs glyphs: UnsafePointer<CGGlyph>, properties props: UnsafePointer<NSLayoutManager.GlyphProperty>, characterIndexes charIndexes: UnsafePointer<Int>, font aFont: NSFont, forGlyphRange glyphRange: NSRange) -> Int { // 把系统生成的字形数组转成可变数组,方便修改 var mutableGlyphs = Array(UnsafeBufferPointer(start: glyphs, count: glyphRange.length)) var mutableProps = Array(UnsafeBufferPointer(start: props, count: glyphRange.length)) // 遍历当前范围内的每个字符 let text = layoutManager.textStorage?.string as NSString? guard let targetText = text else { return 0 } for i in 0..<glyphRange.length { let charIndex = charIndexes[i] let char = targetText.character(at: charIndex) // 检查当前字体是否包含该字符的字形 if !aFont.hasGlyph(for: UnicodeScalar(char)!) { // 替换成空心方框的字形(Unicode U+25A1) if let missingGlyph = aFont.glyph(for: UnicodeScalar(0x25A1)!) { mutableGlyphs[i] = missingGlyph } } } // 把修改后的字形数组设置回布局管理器 layoutManager.setGlyphs(mutableGlyphs, properties: mutableProps, characterIndexes: charIndexes, font: aFont, forGlyphRange: glyphRange) // 返回0表示我们已经处理了这个范围,不需要系统再生成 return 0 }
最后记得把NSTextView的layoutManager的delegate指向你的类:
textView.layoutManager.delegate = self
方案二:提前预处理富文本字符串
如果你不想处理布局管理器的代理逻辑,也可以在把字符串设置到NSTextView之前,提前检查每个字符是否被字体支持,不支持的直接替换成占位符(比如方框)。
示例代码:
func createNoFallbackAttributedString(from rawString: String, with font: NSFont) -> NSAttributedString { let mutableAttrStr = NSMutableAttributedString(string: rawString) mutableAttrStr.beginEditing() for (index, char) in rawString.enumerated() { let charRange = NSRange(location: index, length: 1) // 检查字符是否有对应的字形 if let scalar = char.unicodeScalars.first, !font.hasGlyph(for: scalar) { // 替换成占位符字符 mutableAttrStr.replaceCharacters(in: charRange, with: "□") // 给占位符设置同样的字体,避免触发回退 mutableAttrStr.addAttribute(.font, value: font, range: charRange) } else { mutableAttrStr.addAttribute(.font, value: font, range: charRange) } } mutableAttrStr.endEditing() return mutableAttrStr }
使用的时候直接把返回的富文本设置给textView:
textView.textStorage?.setAttributedString(createNoFallbackAttributedString(from: "需要显示的文本", with: targetFont))
注意事项
font.hasGlyph(for:)方法针对单个Unicode标量有效,但对于组合字符(比如带变音符号的字符、某些emoji),可能需要额外处理,比如检查整个字符序列的字形是否存在。- 如果你想使用系统默认的缺失字符标记,可以用Unicode U+FFFD(替换字符,显示为�),这个字符几乎所有字体都支持。
内容的提问来源于stack exchange,提问作者kamisama42
相关产品推荐
相关产品推荐

