《iOS Core Animation》学习笔记:寄宿图

contents属性

CALayer有一个属性叫做contents,这个属性的类型被定义为AnyObject?,意味着它可以是可空的任意类型对象。在这种情况下,可以给contents属性赋任何值,你的App仍然能够编译通过。但是在实践中,如果给contents赋的不是CGImage,那么图层将是空白的。

contents属性类型之所以被定义为AnyObject?,是因为在Mac OS系统上,这个属性对CGImage和NSImage类型的值都起作用。如果在iOS平台上将UIImage的值赋给它,只能得到一个空白的图层。

基于上一个DemoCode,我们这次直接把layerView的宿主图层的contents属性设置成图片

import UIKit

class ViewController: UIViewController {

@IBOutlet weak var layerView: UIView!

override func viewDidLoad() {
    super.viewDidLoad()
    
    let image = UIImage(named: "snowman")
    
    self.layerView.layer.contents = image?.CGImage
  }
}
UIView的宿主图层中显示一张图片

contentGravity属性

在使用UIImageView的时候遇到过图片被拉伸的情况,解决方案就是把contentMode属性设置成某个合适的值,例如:

imageView.contentMode = .ScaleAspectFit

CALayer与contentMode对应的属性叫做contentsGravity,但是它是一个String类型,而不是一个枚举类型。contentsGravity可选的常用值有以下这些:

  • kCAGravityCenter
  • kCAGravityTop
  • kCAGravityBottom
  • kCAGravityLeft
  • kCAGravityRight
  • kCAGravityTopLeft
  • kCAGravityTopRight
  • kCAGravityBottomLeft
  • kCAGravityBottomRight
  • kCAGravityResize
  • kCAGravityResizeAspect
  • kCAGravityResizeAspectFill

和contentMode一样,contentsGravity的目的是为了决定内容在图层的边界中怎样对齐,假如使用kCAGravityResizeAspect,它的效果等同于UIViewContentModeScaleAspectFit,同时它还能在图层中等比例拉伸以适应图层的边界。

self.layerView.layer.contentsGravity = kCAGravityResizeAspect

contentScale属性

contentScale属性定义了寄宿图的像素尺寸和视图大小的比例,默认情况下它是一个值为1.0的浮点数。

contentScale属性其实属于支持高分辨率屏幕机制的一部分。它用来判断在绘制图层的时候应该为寄宿图创建的空间大小和需要显示的图片的拉伸度。

如果contentScale值为1.0,将会以每一个点1个像素绘制图片,如果设置为2.0,则会以每个点2个像素绘制图片,就是熟知的Retina屏幕。

当用代码的方式来处理寄宿图时候,一定要记住要手动的设置图层的contentScale属性,否则图片在Retina设备上就显得不正确。

self.layerView.layer.contentsScale = UIScreen.mainScreen().scale

masksToBounds属性

UIView有一个叫clipToBounds的属性,可以用来决定是否显示超出边界的内容,CALayer对应的属性叫做masksToBounds,设置值为true,内容就不会超出边界了。

self.layerView.layer.masksToBounds = true
图:使用masksToBounds来修建图层内容

contentsRect属性

CALayer的contentsRect属性允许在图层边框里显示寄宿图的一个子域。

和bounds、frame不同,contentsRect不是按照点来计算的,它使用的是单位坐标,单位坐标指定在0到1之间,是一个相对值(像素和点就是绝对值)。

iOS使用了以下的坐标系统

  • 点:在iOS和Mac OS中最常见的坐标体系。点就像虚拟的像素,也被称作逻辑像素。在标准设备上,一个点就是一个像素,但是在Retina设备上,一个点等于22个像素或33个像素。iOS用点作为屏幕的坐标测算体系就是为了在Retina设备和普通设备上能有一致的视觉效果。
  • 像素:物理像素坐标并不会用来屏幕布局,但是仍然与图片有相对关系。UIImage是一个屏幕分辨率解决方案,所以指定点来度量大小。但是一些底层的图片表示,如CGImage就会使用像素,所以我们要清楚在Retina设备和普通设备上,它们表现出来了不同的大小。
  • 单位:对于与图片大小或是图层边界相关的显示,单位坐标是一个方便的度量方式,当大小改变的时候,也不需要再次调整。单位坐标在OpenGL这种纹理坐标系统中用的很多,Core Animation中也用到了单位坐标。

默认的contentsRect值是{0,0,1,1},意味着整个寄宿图默认都是可见的。

事实上给contentsRect设置一个负数的原点或是大于{1,1}的尺寸也可以。这种情况下,最外面的像素会被拉伸以填充剩下的区域。

下面加入一些代码,可以只显示snowman的右上角四分之一的内容,而layer的大小保持不变,所以显示的内容会被拉伸。

// 选择右上角四分之一为内容
self.layerView.layer.contentsRect = CGRectMake(0.5, 0, 0.5, 0.5)
//拉伸
self.layerView.layer.contentsGravity = kCAGravityResize
显示右上角四分之一内容,且拉伸

contentsCenter属性

从contentsCenter属性名字看,初学者很有可能认为它和图片的位置有关,不过这个名字误导了你。
contentsCenter其实是一个CGRect,它定义了一个固定的边框和一个在图层上可拉伸的区域。
默认情况下,contentsCenter是{0,0,1,1},这意味着如果大小(由contentsGravity决定)改变了,那么寄宿图将会均匀地拉伸。

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

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

下面加入一些代码,基于上个snowman效果,把左下角的四分之一部分进行拉伸

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

也可以在Interface Builder里配置,而不需要写代码

Custom Drawing

为contents赋CGImage值并不是唯一设置寄宿图的方法。我们也可以直接用Core Graphics直接绘制寄宿图。

下面通过代码实现CALayerDelegate来绘制图层

import UIKit

class ViewController: UIViewController {

@IBOutlet weak var layerView: UIView!

override func viewDidLoad() {
    super.viewDidLoad()

    // 创建子layer
    let blueLayer = CALayer()
    blueLayer.frame = CGRectMake(50.0, 50.0, 100.0, 100.0)
    blueLayer.backgroundColor = UIColor.blueColor().CGColor
    
    //设置layer的delegate
    blueLayer.delegate = self
    
    //确保layer的寄宿图使用正确的scale
    blueLayer.contentsScale = UIScreen.mainScreen().scale
    
    self.layerView.layer.addSublayer(blueLayer)
    
    //强制layer重绘
    blueLayer.display()
  }

override func drawLayer(layer: CALayer, inContext ctx: CGContext) {
    
    CGContextSetLineWidth(ctx, 10.0)
    
    CGContextSetStrokeColorWithColor(ctx, UIColor.redColor().CGColor)
    
    CGContextStrokeEllipseInRect(ctx, layer.bounds)
  }
}

注意

  • blueLayer上显示地调用了display()。不同于UIView,当图层显示在屏幕上时,CALayer不会自动重绘它的内容,它把重绘的决定权交给了开发者。
  • 尽管这里没有设置masksToBound属性,绘制的那个圆仍然沿着边界被裁减了。这是因为当使用CALayerDelegate绘制寄宿图的时候,并没有对超出边界外的内容提供绘制支持。

最后除非你创建了一个单独的图层,你几乎没有机会用到CALayerDelegate协议。因为当UIView创建了它的寄宿图层时,它会自动地把图层的delegate设置为自己,并提供了一个displayLayer的实现。

示例代码地址

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

推荐阅读更多精彩内容