NSTextField 文本模糊的解决办法 canDrawSubviewsIntoLayer

在开发落格输入法 macOS 的过程当中,我一直被一个奇怪的问题所困扰——文本模糊。

次像素

无论我怎样调试,落格输入法的候选文字都无法达到原生文字的那样清晰和锐利。在请教了大神之后,得到的活久见经验是——上游次像素渲染 bug。

在很多平台都有类似问题,macOS 底层渲染也有这样的错误,一旦你的布局出现了小数点或者不是整数,就会导致文本渲染模糊。

那么怎么解决呢?只要把位置和大小“对正”就可以了,不要直接使用 Int(xxx) 来转换你的数值,使用 roundf() ,不过,对于 Swift 来说,我们有更方便的方式,比如这样:

rounded() 方法就是我们常说的“四舍五入”,如果你直接转换数据类型到整形,那么小数点后的内容会被直接抛弃,哪怕是 0.99999 ,也会变成 0 ,显然这并不是我们想要的,如果用这个函数来转换,那么你得到的还是 CGFloat ,但值却变成了原本的四舍五入“整数”。

确实好了很多,不会那么模糊了,但如果你仔细看,总感觉还是有哪里不太一样。

实际上,这个  bug 在最新的 macOS 10.14 Mojave 中已经修复了。

canDrawSubviewsIntoLayer

还有的人说,是因为你用了 layer,这样就会导致性能底下,如果把 wantsLayer  关闭,或者给一个非透明的背景,就会好很多。但这终究不是个办法,如果必须要透明呢?总之,我经过查询找到了这个方法 canDrawSubviewsIntoLayer 

苹果官方的文档对它是这么描述的:

当这个属性的值是 true  ,任何任何子视图隐式创建的 layer——也就是说,那些你没有通过设置 wantsLayer  属性为 true  创建的 layer,都会把自己绘制在当前视图的 layer上。换句话说就是子视图不再拥有自己的layer。相反,他们把自身的内容绘制在父视图的 layer中。所有受影响的视图都使用 draw(_:)  方法来绘制内容。它们不再使用 updateLayer()  方法更新 layer 内容,就算 wantsUpdateLayer  属性设置为 true  。

使用这个属性来为基于 layer 的视图和子视图扁平化 layer 继承。扁平化 layer 集成减少了你试图继承中的 layer 数量(并且还可能减少内存的使用)。减少 layer 数量可以在大量视图和子视图内容不明显变化的情况下更加高效。比如说,扁平化继承降低了组合视图时使用的时间。不要扁平化视图继承如果你准备给子视图添加动画的话。

当改变这个属性的值时,当前视图必须拥有一个 layer 对象。该属性默认值为 false  。

我把英文翻译过来了,实际上,就可以理解为是 Photoshop 里的“合并图层”,如果你的视图组合不做大的改变,那么就可以把它们合并绘制,不需要去管什么透明度和渐变了,直接把最终的效果绘制在同一个图层上。大大降低了渲染成本,这样文本也就不再模糊,变得清晰锐利。

发表评论

电子邮件地址不会被公开。 必填项已用*标注