容易忽略的那些小点总结 (四) —— CALayer相关(三)

版本记录

版本号 时间
V1.0 2018.01.22

前言

在苹果的API文档中,有很多属性和方法我们用的不是很多,所以很容易忽略和出错,下面我就用这一个专题专门说一些不常用的API接口,下面开始。感兴趣的可以参考前面几篇文章。
1. 容易忽略的那些小点总结 (一) —— UIView UIViewTintAdjustmentMode相关(一)
2. 容易忽略的那些小点总结 (二) —— CALayer相关(一)
3. 容易忽略的那些小点总结 (三) —— CALayer相关(二)

contentsGravity

/* A string defining how the contents of the layer is mapped into its
 * bounds rect. Options are `center', `top', `bottom', `left',
 * `right', `topLeft', `topRight', `bottomLeft', `bottomRight',
 * `resize', `resizeAspect', `resizeAspectFill'. The default value is
 * `resize'. Note that "bottom" always means "Minimum Y" and "top"
 * always means "Maximum Y". */

  一个字符串定义了layer的contents是如何映射到它的边界矩形内部的,选项有`center', `top', 
  `bottom', `left', `right', `topLeft', `topRight', `bottomLeft', `bottomRight',
   `resize', `resizeAspect', `resizeAspectFill',默认值是`resize',
  注意"bottom"意思是"Minimum Y","top"的意思是"Maximum Y"。

@property(copy) NSString *contentsGravity;

contentsGravity可以去下面的值

/** Layer `contentsGravity' values. **/

CA_EXTERN NSString * const kCAGravityCenter
    CA_AVAILABLE_STARTING (10.5, 2.0, 9.0, 2.0);
CA_EXTERN NSString * const kCAGravityTop
    CA_AVAILABLE_STARTING (10.5, 2.0, 9.0, 2.0);
CA_EXTERN NSString * const kCAGravityBottom
    CA_AVAILABLE_STARTING (10.5, 2.0, 9.0, 2.0);
CA_EXTERN NSString * const kCAGravityLeft
    CA_AVAILABLE_STARTING (10.5, 2.0, 9.0, 2.0);
CA_EXTERN NSString * const kCAGravityRight
    CA_AVAILABLE_STARTING (10.5, 2.0, 9.0, 2.0);
CA_EXTERN NSString * const kCAGravityTopLeft
    CA_AVAILABLE_STARTING (10.5, 2.0, 9.0, 2.0);
CA_EXTERN NSString * const kCAGravityTopRight
    CA_AVAILABLE_STARTING (10.5, 2.0, 9.0, 2.0);
CA_EXTERN NSString * const kCAGravityBottomLeft
    CA_AVAILABLE_STARTING (10.5, 2.0, 9.0, 2.0);
CA_EXTERN NSString * const kCAGravityBottomRight
    CA_AVAILABLE_STARTING (10.5, 2.0, 9.0, 2.0);
CA_EXTERN NSString * const kCAGravityResize
    CA_AVAILABLE_STARTING (10.5, 2.0, 9.0, 2.0);
CA_EXTERN NSString * const kCAGravityResizeAspect
    CA_AVAILABLE_STARTING (10.5, 2.0, 9.0, 2.0);
CA_EXTERN NSString * const kCAGravityResizeAspectFill
    CA_AVAILABLE_STARTING (10.5, 2.0, 9.0, 2.0);

cotentMode一样,contentsGravity的目的是为了决定内容在图层的边界中怎么对齐,kCAGravityResizeAspect的效果等同于UIViewContentModeScaleAspectFit, 同时它还能在图层中等比例拉伸以适应图层的边界。默认值是kCAGravityResize

重要:1) contents gravity常数的命名是基于垂直轴的方向。 如果您使用垂直分量的重力常量,例如 kCAGravityTop,你也应该检查图层的contentsAreFlipped。 如果是YES,则kCAGravityTop将内容对齐到图层的底部,kCAGravityBottom将内容对齐到图层的顶部。2) macOS和iOS中视图的默认坐标系统在纵轴的方向上有所不同:在macOS中,默认坐标系的原点位于绘图区域的左下方,往上是正值,在iOS中默认坐标系统的起点位于绘图区域的左上角,往下是正值。 3) 有关更多信息,请参阅Coordinate system

图1显示了为图层的contentsGravity属性设置不同值的效果的四个示例。

Different effects of setting a layer's contents gravity

contentsScale

先看一下这个属性的API文档。

/* Defines the scale factor applied to the contents of the layer. If
 * the physical size of the contents is '(w, h)' then the logical size
 * (i.e. for contentsGravity calculations) is defined as '(w /
 * contentsScale, h / contentsScale)'. Applies to both images provided
 * explicitly and content provided via -drawInContext: (i.e. if
 * contentsScale is two -drawInContext: will draw into a buffer twice
 * as large as the layer bounds). Defaults to one. Animatable. */

定义应用于图层内容的比例因子。 如果内容的物理尺寸是“(w,h)”,那么逻辑尺寸(即用于contentGravity计算)被定义为'(w / contentsScale,h / contentsScale)'。 适用于显式提供的图像和通过-drawInContext提供的内容(即如果contentsScale是2,-drawInContext:将绘制到缓冲区大小为layer bounds的2倍), 默认为1,可动画。

@property CGFloat contentsScale
  CA_AVAILABLE_STARTING (10.7, 4.0, 9.0, 2.0);

也可以这么理解

  • 该值定义了图层的逻辑坐标空间(以点为单位)和物理坐标空间(以像素为单位)之间的映射。 较高的比例因子表示在渲染时间层中的每个点由多于一个像素表示。 例如,如果比例因子为2.0,图层边界为50 x 50点,则用于呈现图层内容的位图大小为100 x 100像素。

  • 此属性的默认值是1.0。 对于附加到视图的图层,视图自动将比例因子自动更改为适合当前屏幕的值。 对于您自己创建和管理的图层,您必须根据屏幕的分辨率和您提供的内容自行设置此属性的值。Core Animation使用您指定的值作为提示来确定如何呈现您的内容。


还是先看一下这个属性的API文档

/* A rectangle in normalized image coordinates defining the scaled
 * center part of the `contents' image.
 *
 * When an image is resized due to its `contentsGravity' property its
 * center part implicitly defines the 3x3 grid that controls how the
 * image is scaled to its drawn size. The center part is stretched in
 * both dimensions; the top and bottom parts are only stretched
 * horizontally; the left and right parts are only stretched
 * vertically; the four corner parts are not stretched at all. (This is
 * often called "9-slice scaling".)
 *
 * The rectangle is interpreted after the effects of the `contentsRect'
 * property have been applied. It defaults to the unit rectangle [0 0 1
 * 1] meaning that the entire image is scaled. As a special case, if
 * the width or height is zero, it is implicitly adjusted to the width
 * or height of a single source pixel centered at that position. If the
 * rectangle extends outside the [0 0 1 1] unit rectangle the result is
 * undefined. Animatable. */

标准化图像坐标中的矩形定义“contents”图像的缩放中心部分。 当由于其“contentsGravity”属性而调整图像大小时
中心部分隐含地定义了3x3网格,其控制如何将图像缩放到其绘制的大小。 中间部分在两个方向都被拉伸; 顶部和底部只有水平伸展; 左右部分只是垂直拉伸; 四个角落部分根本不被拉伸。 (这通常被称为“9-slice scaling”)。
在应用“contentsRect”属性的效果之后,该矩形被解释。 它默认为单位矩形[0 0 1 1],意味着整个图像被缩放。 作为特殊情况,如果宽度或高度为零,则隐式地调整为以该位置为中心的单个源像素的宽度或高度。 如果矩形延伸到[0 0 1 1]单位矩形之外,则结果是不确定的,可动画。

@property CGRect contentsCenter;

也可以这么理解

  • 如果图层内容被调整,定义如何调整图层内容的缩放比例的矩形,可动画。

  • 您可以使用此属性将图层的内容细分为3x3网格。此属性中的值指定该网格中的中心矩形的位置和大小。如果图层的contentsGravity属性设置为调整大小模式之一,则调整图层大小会导致缩放在网格的每个矩形中以不同的方式出现。中心矩形在两个方向上都被拉伸,顶部中心矩形和底部中心矩形仅被水平拉伸,左中心矩形和右心矩形仅被垂直拉伸,并且四角矩形根本不被拉伸。因此,您可以使用这种技术来实现使用三部分或九部分图像的可拉伸背景或图像。

  • 此属性中的值默认设置为单位矩形(0.0,0.0)(1.0,1.0),这将导致整个图像在两个维度上缩放。如果指定一个在单位矩形外延伸的矩形,则结果是未定义的。只有将contentsRect属性应用于图像后,才能应用指定的矩形。

注意:如果此属性中矩形的宽度或高度非常小或为0,则该值将隐式更改为以指定位置为中心的单个源像素的宽度或高度。

我们先正常的显示一个图片,看一下代码

//创建CGImageRef(test.png是工程Bundle中的资源图片文件)
CGImageRef img = [UIImage imageNamed:@"test.png"].CGImage;
//创建CALayer
CALayer *layer = [CALayer layer];
//设置CALayer的内容
layer.contents = (__bridge id)img;
layer.frame = CGRectMake(20, 20, CGImageGetWidth(img), CGImageGetHeight(img));
//添加Layer到View
[self.view.layer addSublayer:layer];

运行起来图形如下所示

下面我们在设置点东西

//选择右上角的四分之一为内容
layer.contentsRect = CGRectMake(0.5, 0, 0.5, 0.5);
//拉伸
layer.contentsGravity = kCAGravityResize;

这就会产生一个效果,让整个Layer只显示右上角四分之一的内容,而Layer的大小保持不变,所以显示的内容会被拉伸,如下所示。

下面就可以讲CALayer的contentsCenter属性了,因为contentsCenter属性只有在图片被拉伸后才会起作用,contentsCenter可以用来定义全面拉伸的范围。什么是“全面拉伸”?看一下下图

  • 如果contentsCenter属性是上图中间的蓝色方框,那么当这个图片被拉伸后,contentsCenter属性定义的区域会被全面拉伸(也就是从四个方向进行放大或者缩小),而被这个方框分割后的其他方格会按照上图所表示的进行横向或者纵向的拉伸,或者某些方框根本不拉伸!这就是contentsCenter属性的意义。

  • contentsCenter属性和contentsRect属性一样,同样是以比例作单位。两个属性可以叠加,如果contentsRect属性被设置,contentsCenter属性就会操作contentsRect属性所定义的范围。

那么,接着上面的代码,我们想把上图中的左下角的四分之一部分进行全面拉伸,那么加入设置contentsCenter属性的代码:

//左下角的四分之一拉伸
layer.contentsCenter = CGRectMake(0, 0.5, 0.5, 0.5);

contentsFormat

还是先看一下这个属性的API

/* A hint for the desired storage format of the layer contents provided by
 * -drawLayerInContext. Defaults to kCAContentsFormatRGBA8Uint. Note that this
 * does not affect the interpretation of the `contents' property directly. */

-drawLayerInContext提供的图层内容的所需存储格式的提示。 缺省为kCAContentsFormatRGBA8Uint。 
请注意,这不会直接影响“contents”属性的解释。

@property(copy) NSString *contentsFormat
  CA_AVAILABLE_STARTING (10.12, 10.0, 10.0, 3.0);

你也可以这么理解

  • 这个属性的默认值是kCAContentsFormatRGBA8Uint
  • UIView和图层支持的NSView对象可能会将值更改为适合当前设备的格式。

这个值可以有三个选择,如下所示

/** Layer `contentsFormat` values. **/

CA_EXTERN NSString * const kCAContentsFormatRGBA8Uint /* RGBA UInt8 per component */
  CA_AVAILABLE_STARTING (10.12, 10.0, 10.0, 3.0);
CA_EXTERN NSString * const kCAContentsFormatRGBA16Float /* RGBA half-float 16-bit per component */
  CA_AVAILABLE_STARTING (10.12, 10.0, 10.0, 3.0);
CA_EXTERN NSString * const kCAContentsFormatGray8Uint /* Grayscale with alpha (if not opaque) UInt8 per component */
  CA_AVAILABLE_STARTING (10.12, 10.0, 10.0, 3.0);

这个很好理解了,就是色值的组成形式,比如说kCAContentsFormatRGBA8Uint就表示每一位都是UInt8类型的数据。


minificationFilter和magnificationFilter

有时候,显示一个非真实大小的图片确实是我们需要的效果。比如说一个头像或是图片的缩略图,再比如说一个可以被拖拽和伸缩的大图。这些情况下,为同一图片的不同大小存储不同的图片显得又不切实际。

当图片需要显示不同的大小的时候,有一种叫做拉伸过滤的算法就起到作用了。它作用于原图的像素上并根据需要生成新的像素显示在屏幕上。

还是先看一下这个属性的API

/* The filter types to use when rendering the `contents' property of
 * the layer. The minification filter is used when to reduce the size
 * of image data, the magnification filter to increase the size of
 * image data. Currently the allowed values are `nearest' and `linear'.
 * Both properties default to `linear'. */

渲染图层的“contents”属性时使用的过滤器类型。 minification过滤器用于缩小图像数据的大小时,
magnification过滤器增加图像数据的大小。 目前允许的值是`nearest' 和 `linear'的。 这两个
属性默认为`linear'。

@property(copy) NSString *minificationFilter;
@property(copy) NSString *magnificationFilter;

它们可选的值如下所示:

/** Contents filter names. **/

CA_EXTERN NSString * const kCAFilterNearest
    CA_AVAILABLE_STARTING (10.5, 2.0, 9.0, 2.0);
CA_EXTERN NSString * const kCAFilterLinear
    CA_AVAILABLE_STARTING (10.5, 2.0, 9.0, 2.0);

/* Trilinear minification filter. Enables mipmap generation. Some
 * renderers may ignore this, or impose additional restrictions, such
 * as source images requiring power-of-two dimensions. */

CA_EXTERN NSString * const kCAFilterTrilinear
    CA_AVAILABLE_STARTING (10.6, 3.0, 9.0, 2.0);

也可以这么理解

Figure 1 Circle with different magnification filters

左边的圆圈使用的是kCAFilterLinear,右边使用的是kCAFilterNearest

  • kCAFilterLinear过滤器采用双线性滤波算法,它在大多数情况下都表现良好。双线性滤波算法通过对多个像素取样最终生成新的值,得到一个平滑的表现不错的拉伸。但是当放大倍数比较大的时候图片就模糊不清了。

  • kCAFilterTrilinearkCAFilterLinear非常相似,大部分情况下二者都看不出来有什么差别。但是,较双线性滤波算法而言,三线性滤波算法存储了多个大小情况下的图片(也叫多重贴图),并三维取样,同时结合大图和小图的存储进而得到最后的结果。

对于大图来说,双线性滤波和三线性滤波表现得更出色
  • kCAFilterNearest是一种比较武断的方法。从名字不难看出,这个算法(也叫最近过滤)就是取样最近的单像素点而不管其他的颜色。这样做非常快,也不会使图片模糊。但是,最明显的效果就是,会使得压缩图片更糟,图片放大之后也显得块状或是马赛克严重。
对于没有斜线的小图来说,最近过滤算法要好很多
  • 总的来说,对于比较小的图或者是差异特别明显,极少斜线的大图,最近过滤算法会保留这种差异明显的特质以呈现更好的结果。但是对于大多数的图尤其是有很多斜线或是曲线轮廓的图片来说,最近过滤算法会导致更差的结果。换句话说,线性过滤保留了形状,最近过滤则保留了像素的差异。

下面我们看一个别人提供的例子。

kCAFilterLinear
kCAFilterNearest

参考文章

1. iOS: 关于CALayer的contentsCenter属性
2. iOS: 使用CALayer的layoutSublayers方法来规划子Layer
3. iOS: CALayer的timeOffset在speed设置成0后不起作用
4. iOS: 视觉化CALayer的zPosition
5. iOS: CATextLayer的sizeToFit方法
6. 4.5 拉伸过滤

后记

本篇已结束,后面更精彩,欢迎关注~~~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,723评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,485评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,998评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,323评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,355评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,079评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,389评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,019评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,519评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,971评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,100评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,738评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,293评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,289评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,517评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,547评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,834评论 2 345

推荐阅读更多精彩内容