0%

点和像素

在 iOS 开发中,我们布局一个 UIView 和 CoreGraphics 绘制内容的时候,使用的单位是点(Point, 缩写 pt),而屏幕上的显示单位是像素(Pixel, 缩写 px)。
点和像素的换算规则取决于屏幕的精细程度(PPI:屏幕上每英寸可以显示的像素点的数量),比如:

  • iPhone 3GS 中一点等于一像素
  • iPhone 4 中一点等于两像素
  • iPhone 6 Plus 中一点等于三像素

这个比例值我们可以通过 CGFloat screenScale = [UIScreen mainScreen].scale 获取 。

什么是像素对齐

通常我们指定一个 UIView 的 frame 会采用整数,由于点是像素的整数倍,所以这里像素是对齐的。
但是有的时候我们的 frame 是通过计算出来的,就会出现小数,这个小数乘以 screenScale 得出的数值可能不是整数,这就是像素不对齐。
还有一种特殊的像素不对齐,是使用 CoreGraphics 绘制内容时特有的,后面会详细说明。

UIView 的像素对齐

UILabel 的试验

我们设置一个 UILabel :

1
2
3
4
UILabel *label = [[UILabel alloc] init];
label.text = @"UIView 的像素不对齐";
label.frame = CGRectMake(100, 100, 300, 500.001);
[self.view addSubview:label];

通过 iPhone 13 Pro Max 的模拟器打开,在模拟器菜单中打开 Debug - Color Misaligned Images 选项,你会发现这个 UILabel 的背景变成黄色:
这是因为系统发现了 500.001 是一个像素不对齐的高度,将它标记它为黄色。
同样的,(100.1, 100, 300, 500),(100, 100.1, 300, 500) 都会有问题。
修正为 (100, 100, 300, 500) 就没问题了。
还有 (100, 100, 300, 500.33333) 也是没问题的,因为 500.33333 * 3 = 1501 是整数。
神奇的是,(100, 100, 300.1, 500) 是没问题的,猜测是 UILabel 的自动宽度导致(没有验证)。

不对齐会怎么样

有同学可能会问,对齐不对齐会有什么问题呢?好像也没什么影响,看起来上面的 UILable 也显示很正常。
我们采用「像素眼」来看看:(100, 100, 300, 500) vs (100, 100, 300, 500.1)
很明显,右边指定的高度高,渲染的时候需要多填充一个像素,而填充内容是一些半透明的像素,主观来说就是「右边糊了」。

再看:(100, 100, 300, 500) vs (100.1, 100, 300, 500)
这对比就更加夸张了,各种细节不一样,这是 x 处于像素点的中间位置,结果引发了更多抗锯齿来补充过渡颜色。

为什么会糊

让我们再进一步调查一下为什么会糊?
之前有个普遍猜想,当我们设置 x 为 100.1 或者 100.9 的时候,系统会自动帮我们取整数像素单位来显示,这对吗?
对的,但是跟我们想的可能有点不一样。
我们来对比一下 (100.1, 100, 300, 500) vs (100.9, 100, 300, 500):
我们可以很显著的发现,最左边那条竖线的颜色不一致,这是为什么呢?
先用苹果官方的话来解释一下:
简单理解一下就是,如果你非得从 0.5 像素的位置开始画线,系统也会帮你画,只是会触发抗锯齿(antialiasing),也就是会帮你把半个像素补齐到一个像素

补齐规则是什么呢?
苹果没说,我根据试验结论猜测一下:根据该像素的占用面积的比例乘上原始颜色渲染出一个新的像素,比如苹果这个示例中就是 0.5 * 黑色,得出一个灰色。
结合上述论证与猜想,可以推测出上面文字 100.1 vs 100.9 竖线颜色不一的问题了。
根据我们前面的推测,100.1 占了 90% 像素面积,于是就是 90% 颜色深度,而 100.9 占了 10% 像素面积,也就是 10% 颜色深度。
所以,肉眼可见 100.1 比 100.9 深了许多。

其他视图

如果去尝试其他视图的情况,可以发现 UIImageView,UIButton 只要设置了图片或文字,都会有上述情况。
一个 UIView 只设置了背景颜色,那么像素不对齐也不会有什么问题。但是,用 CoreGraphics 画上一条蓝线,就会出现问题了。
所以得出结论:UIView 在有内容的情况下,像素不对齐就会触发抗锯齿,就会导致模糊现象。如果没有内容,只是设置了背景色则不会有问题。

CoreGraphics 的像素对齐

CoreGraphics 的像素对齐问题,又是什么情况呢?
假设我们用 CoreGraphics 在 (3, 0 ) 到 (3, 5) 画一条一像素宽的线时,这条线会落在哪里?
让我们魔改一下苹果的原图,会更好理解:
很明显,一倍屏的情况下,是在第三个点(point)上左右平分半个像素。
半个像素,是不是想起了什么?没错,触发了抗锯齿,于是你的一像素黑线变成了二像素灰线。
怎么解决呢?有两个方法:

  1. 向左或向右移动半个像素,在不同屏幕下通用公式是: ±(1 / screenScale / 2)
  2. 把线的宽度补齐到两个像素,这样你会获得一条两个像素的黑线

推广一下:使用 CoreGraphics 绘制奇数像素宽/高度线的时候,像素不对齐,会触发抗锯齿,导致模糊。

注意,偶数宽/高度的情况下,因为绘制内容会均匀地落在坐标两边的像素点中,不需要也不能去做奇数时的处理方案。
再来魔改一下苹果的图,方便大家理解偶数的情况:
最终还有一个点容易搞错,CoreGraphics 绘制问题不要运用到 UIView 体系中。
因为 CoreGraphics 画线的时候在宽度上是没有方向性的,所以在 x 点画一条线的时候,是以 x 为中线,左右平均分配线宽。
而 UIView 在布局的时候是有坐标系的,当我们指定一个 (3, 0, 1, 10) 的矩形框的时候(一倍屏),可以准确地在 3 像素开始向右边画出一个一像素宽的矩形(也就是一像素线)。

总结

所以,再次提炼一下「像素对齐」:当我们绘制视图时,应该让内容填满像素格。否则会触发了抗锯齿,可能导致内容模糊。

参考文档

iOS Drawing Concepts
iOS 绘制1像素的线
iOS优化:解决iOS中像素不对齐问题
iOS Color Misaligned Images优化
Aligned UIViews

思考

年初离开美图公司,进入了一段比较自由的工作时期。
略微松弛之后,反而有了慌张的感觉,开始有空思考之前担忧的两个职业问题:

  • 35岁之后竞争力何在?
  • 作为 iOS 开发者,苹果不行了,该怎么办?

其实之前,我有一些自以为是的应对之道:

  • 35岁就该有35岁开发者该具备的技能,35岁还是25岁的水平,活该被淘汰。
  • 苹果的平台就算被淘汰,也应该是被一个全新的平台淘汰,那我们就是转型新平台最容易的一批人。

仔细想想,以上应对都建立在一个核心能力上:拥有扎实的基础知识。
基础知识可以帮助我解决深度问题,也就是初级开发者解决不了的问题。
基础知识可以让我在不同平台开发中轻松切换,不局限于开发语言,或者平台特性。

基础知识

基础知识其实是一个很宽泛的概念,该怎么定义呢?
我的答案是,尽量不考虑具体平台特性,从代码到机器运行的每个过程:

  • 设计模式
  • 从高级语言编写,到编译、汇编、链接成为机器码的过程
  • 数据结构与算法
  • 操作系统
  • 计算机组成原理
  • 基础硬件知识
  • 简单的数字电路知识

还有很多我没罗列到的,比如网络知识。但是,我想暂时只考虑我感兴趣的。

接下来是我今年学习的课程:

Crash Course Computer Science - 概览计算机科学

  • 难度:⭐
  • 耗时:20天左右
  • 获取方式:油管原版 B站中文版
  • 概述:该课程非常适合快速预览计算机基础知识,可以从中选取感兴趣的方面,再做深入了解。也可以快速帮我回顾计算机的发展史。
  • 学习情况:很轻松地学完了整个课程。

清华的操作系统原理 - 了解操作系统

  • 难度:⭐⭐
  • 耗时:40天左右
  • 获取方式:B站
  • 概述:该课程非常细致的讲述了操作系统的方方面面,内容总体较为浅显易懂,很适合普通开发者作为入门。
  • 学习情况:较为正常地学完了整个课程,其中有一些逻辑推演需要反复学习,特别是多线程相关。

操作系统实战45讲 - 深入操作系统

  • 难度:⭐⭐⭐⭐⭐
  • 耗时:???(还没学完)
  • 获取方式:极客时间
  • 概述:该课程非常深入地介绍了怎么开发一个操作系统,难度很高。
  • 学习情况:目前还没学完。为了学习该课程,我不得不去补充很多相关知识,比如操作系统基础知识,部分硬件基础知识。

数据结构与算法之美 - 重拾数据结构与算法

  • 难度:⭐⭐⭐
  • 耗时:40天左右
  • 获取方式:极客时间
  • 概述:该课程非常详细地介绍了常用的数据结构与算法。理论结合例子,再匹配上课程中非常好的逻辑分析,让我觉得算法也没那么难了。最难得的是,将数据结构和算法的关联性反复推演,让我理解了为什么两者是密不可分的,是很好的数据结构与算法入门课程。
  • 学习情况:有一定难度,其中有一些算法推演需要反复学习,有部分不常用且难懂的算法只做了解没有完全掌握。只写了一个课后作业,课后作业基本上靠自己思考,再对答案。

设计模式之美 - 重拾设计模式

  • 难度:⭐⭐⭐⭐
  • 耗时:40天左右
  • 获取方式:极客时间
  • 概述:该课程不像市面上流通的设计模式介绍课程,不仅仅是介绍,还是通过实战演练,层层深入设计模式的内核。结合了一些源码作为例子,深入剖析了常用的设计模式。比之于招式,该课程更像内功。
  • 学习情况:有一定难度,许多章节需要反复学习。课后作业基本上靠自己思考,再对答案。

编程前你最好了解的基本硬件和计算机基础知识第一季 - 了解模拟电路

  • 难度:⭐⭐⭐
  • 耗时:10天左右
  • 获取方式:B站
  • 概述:该课程以非常生动有趣的方式讲了模拟电路,非常适合作为入门课程。
  • 学习情况:内容本身有一定难度,但是课程非常好,把学习曲线打平了。

编程前你最好了解的基本硬件和计算机基础知识第二季 - 了解数字电路

  • 难度:⭐⭐⭐
  • 耗时:10天左右
  • 获取方式:B站
  • 概述:该课程以非常生动有趣的方式讲了数字电路,非常适合作为入门课程。
  • 学习情况:内容本身有一定难度,但是课程非常好,把学习曲线打平了。

从0到1设计一台计算机 - 概述计算机组成原理

  • 难度:⭐⭐⭐
  • 耗时:10天左右
  • 获取方式:B站
  • 概述:该课程以非常生动有趣的方式讲了怎么做一台计算机,也可以算组成原理的入门。
  • 学习情况:内容本身有一定难度,但是课程非常好,把学习曲线打平了。

小码哥教育周末汇编班 - 入门汇编

  • 难度:⭐⭐⭐
  • 耗时:40天左右
  • 获取方式:B站 (不是完全版本,完全版请自行搜索)
  • 概述:该课程非常非常细致,有时候略显啰嗦地讲了汇编基础内容。从 x86 汇编讲起,扩展到了多个架构的汇编特性。
  • 学习情况:作者靠极其细致的讲解,强行打平了汇编学习的陡峭曲线,是很好的汇编入门课程。

总结

内容方面。以上课程,都是我认为很好的,好在几个方面:

  • 内容言之有物。
  • 结合了实际生产环境。
  • 概念推理的时候,往往会先抛出一个粗浅但简单的方案,再逐步去完善。

平台方面:

  • B站真是一个好平台,提供了大量优质且免费的内容。
  • 极客时间的大部分课程内容不错,但是有部分课程注水了,最好认真试读完再购买。
  • 小码哥教育有许多围绕 iOS 开发相关的课程,可以挑选部分课程作为内容补充,方便 iOS 开发入门基础课程,比如汇编。

时间方面:
二娃之后,我没有多少业余时间,基本上靠着每天遛娃的时间听音频完成学习,需要看图或者看代码的地方,就掏出手机看一眼。一开始还很不习惯,后面慢慢渐入佳境,竟然也习惯了这种学习模式,每到遛娃的时候,竟然也有点兴奋。当然,很多需要敲代码的地方就没办法跟着操作了,自然效果是打折扣的。不过,再怎么样也算是在慢慢进步。

希望新的一年,也可以持续进步💪🏻