iOS核心动画高级技巧之CALayer(一)

iOS核心动画高级技巧之图层变换和专用图层(二)
iOS核心动画高级技巧之核心动画(三)
iOS核心动画高级技巧之性能(四)
iOS核心动画高级技巧之动画总结(五)

  UIView和CALayer的关系
  在iOS中一个UIView对应着一个CALayer,视图的职责就是创建并管理这个图层,以确保当子视图在层级关系中添加或者被移除的时候,他们关联的图层也同样对应在层级关系树当中有相同的操作.实际上这些背后关联的图层才是真正用来在屏幕上显示和做动画,UIView仅仅是对它的一个封装,提供了一些iOS类似于处理触摸的具体功能,以及Core Animation底层方法的高级接口。(CALayer并不能响应事件,它提供了几个能判断一个触点时候再图层的范围之内)

  CALayer存在的意义
  之所以要提供CALayer和UIView这两个平行层级呢,一方面这样可以做到职责分离,可以避免很多重复的代码,另一方面由于iOS和Mac OS的界面其实没多大差别的,但是由于iOS的多点触摸的用户界面和Mac基于鼠标键盘有着本质区别,所以提供一个公用的CALayer来提供界面,分别提供UIView和NSView来提供事件

  CALayer的使用
  平时我们是这样使用UIView的:

1 let blueView = UIView(frame: CGRectMake(100, 100, 100, 100))
2 blueView.backgroundColor = UIColor.blueColor()
3 self.view.addSubview(blueView)

  而我们可以这样使用CALayer,你可以直接用下面这段话直接替换掉上面的代码,程序在外观上不会有任何区别

1 let blueLayer = CALayer()
2 blueLayer.frame = CGRectMake(100, 100, 100, 100)
3 blueLayer.backgroundColor = UIColor.blueColor().CGColor
4 self.view.layer.addSublayer(blueLayer)

  CALayer内容(contents)相关属性
contents属性
  layer有一个contents属性,它需要传入一个id(AnyObject!)类型,这是由于它在iOS平台需要CGImage而Mac需要NSImage,在OC中你需要用id类型强转一下,在Swift中你只需要直接赋一个CGImage就可以了,因为任何一个Class类型的对象都能赋值给AnyObject,如果你传入其它对象,程序不会报错,只是图片不会显示出来,UIImageView之所以能显示图片内部也是使用了这个contents属性的缘故

contentGravity属性
  contentGravity属性对应于view的contentMode属性,可以控制layer怎样对应和拉伸,虽然它的值是字符串,但是swift帮它提供了常量字符串把每个字符串对应了起来

contentsScale属性
  contentsScale属性和UIView中的contentScaleFactor是对应的,它决定了一个图片和视图的比例,即屏幕一个点显示几个像素,这是iOS设备做屏幕适配的原理,一个UIImage是包含scale,direction等信息,而转化成CGImage会丢失这些信息,自己可以通过contentsScale属性把image.scale设置给它.

maskToBounds属性
  maskToBounds属性对应于CALayer的masksToBounds属性,如果设置为true,外部就裁剪了

contentsRect属性
  contentsRect属性允许我们在图层里显示图片的一部分,这个图片的裁剪区域就是这个属性,它是一个CGRect,利用它你可以做图片拼合(即把一套图片集合在成一张图片,再对这个图片裁剪处理了再使用),这样在内存使用/载入时间/渲染性能等方面都有优势,它的值是按比例的,最大是1.

contentsCenter属性
  contentsCenter属性对应着UIImage的resizableImageWithCapInsets,它的值是一个CGRect,它代表的放大的区域,它的效果是党contentScale放大的时候,只放大contentsCenter区域,其它区域压缩

  iOS绘图
  CGImage并不是唯一可以赋值给contents属性的,也可以使用Core Graphics绘制寄宿图给它,如果你实现了drawRect方法,然后如果你调用setNeedsDisplay或者外观属性被改变时,它就会自动调用drawRect自动重绘,虽然drawRet是一个UIView方法,但是其实都是底层都是CALayer重绘保存了图片,如果你不需要自定义绘制就不要写一个空的drawRect方法,它很消耗cpu和内存资源
CALayer有一个可选的delegate属性,如果设置了delegate,并主动调用了layer的displey方法(注意和drawRect不同这个重绘时机是开发者自己控制的,也可以调用setNeedsDisplay方法给系统自己找时机调用),它会调用displayLayer(layer:CALayer!)方法,在这里是设置contents属性的最后机会了,如果你没有实现这个方法,它会尝试去调用下面这个方法:drawLayer(layer:CALayer!,inContext ctx:CGContext!),如果你实现了displayLayer方法,下面这个方法就不会调用了,drawLayer这个方法里你可以做绘图

1 override func drawLayer(layer: CALayer!, inContext ctx: CGContext!) {
2 CGContextSetLineWidth(ctx, 10.0)
3 CGContextSetStrokeColorWithColor(ctx, UIColor.redColor().CGColor);
4 CGContextStrokeEllipseInRect(ctx, layer.bounds)d060557943
5 }
6 override func displayLayer(layer: CALayer!) {
7 layer.contents = UIImage(named: "11.png")?.CGImage
8 }

    由于一个UIView它会把它对应的CALayer的delegate设置为它自己,所以你不能再设置其它的layer的delegate为它,在UIView中都用drawRect方法,而delegate的使用只能单独的使用一个层.

  图层几何学

UIView的frame/bounds/center对应CALayer的frame/bounds/position,center和position是对应父图层的anchorPoint的所在位置,UIView的frame/bounds/center仅仅是存取方法,操纵UIView的这几个属性其实是改变CALayer对应的这几个属性.而CALayer的frame属性又是个计算属性,它是根据bounds/position/transform三个属性计算出来的,而你改变frame的值也可能影响到其中的值,如果你做旋转和缩放后frame和bounds可能不再一致了,bounds就是宽高,而frame还要计算旋转后x和y轴占的空间,如下图

  CALayer通过anchorPoint(锚点)和center(position)对齐来控制UIView的位置,锚点是相对UIView的一个位置,而center就是一个点,由于anchorPoint属性对UIView是屏蔽的,而anchorPoint默认值又是{0.5,0.5},所以这个属性才叫center.而UIView和CALayertransform旋转也是围绕这anchorPoint旋转的,这时候如果是一个圆周运动(比如说时钟旋转)就需要设置锚点的值,让它正常旋转.如下图

            

  在CALayer和UIView都有一套可以把它相对于当前父图层的位置转换成相对其它图层(或view)的位置,Mac OS和iOS的坐标系统是相反的,iOS左上Mac OC左下,你可以用layer的geometryFlipped属性来适配,它可以翻转坐标系.layer的zPosition属性是设置它垂直坐标轴的位置的,默认都是0,所以你只要设置为1,就会显示在其它层的上面

  可以通过相对坐标转换判断点击的点是否在一个layer或view上,代码:

 var point = (touches as NSSet).anyObject()?.locationInView(self.view)
point = blueLayer.convertPoint(point!, fromLayer: self.view.layer) if blueLayer.containsPoint(point!) {
println("touch in blue") let yellowPoint = yellowLayer.convertPoint(point!, fromLayer: blueLayer)
if yellowLayer.containsPoint(yellowPoint) {
println("touch in yellow")
} let redPoint = redLayer.convertPoint(point!, fromLayer: blueLayer)
if redLayer.containsPoint(redPoint) {
println("touch in red")
}
}

  hitTest可以获取你接触的那个图层:

let point = (touches as NSSet).anyObject()?.locationInView(self.view)
let layer = self.view.layer.hitTest(point!) if layer == blueLayer {
println("touch in blue")
}
if layer == yellowLayer {
println("touch in yellow")
}
if layer == redLayer {
println("touch in red")
}

  UIView可以通过autoresizingMask和constraints等属性做到自适应屏幕旋转,CaLayer也有对应的layoutManager属性和CAConstraintLayoutManager类,但是只能在Mac OS上使用,iOS上还不支持,如果你想使用这个特性你就不能单独使用layer,但是如果你想调整layer的大小还是可以通过设置layer的delegate,然后实现代理方法layoutSublayersOfLayer直接修改大小颜色之类,它也需要调用setNeedsLayout方法,它和UIView对应的layoutSubviews是一样的.

  视觉效果
圆角
  layer的cornerRadius属性可以设置圆角曲率,如果曲率大小为边长的一半,圆角会内切于这条边,如果是个正方形最后的结果就是个圆形,如果是裁剪子视图也是直接按圆内裁剪

边框
  layer的borderColor设置边框颜色,它是CGColorRef,borderWidth设置边框宽度,值得注意的是边框宽度占用的是layer的frame的宽度,它并不会在layer外面加一层边框而是在内部生成.而layer会被子layer覆盖但边框不会被覆盖,并且边框只是显示用的,它不会干扰触摸和事件,有没有边框都是一样的.

阴影
  阴影至少需要shadowColor/shadowOffset/shadowOpacity三个属性才能起作用,shadowOffset的值是CGSize类型,两个正值是朝右下角.还有一个属性shadowRadius,它控制阴影的边界模糊程度,默认值是3,值为0则不模糊,值越大越模糊,模糊的结果是CGSize放心颜色重,其它几个方向也有对应的阴影,会有一个层次感.
因为阴影是在layer外部的,所以如果要裁剪超出layer的子视图则需要使用maskToBounds属性,而这时阴影也会被裁剪掉,所以当maskToBounds和阴影共存时需要特殊处理下:

 blueLayer = CALayer()
blueLayer.frame = CGRectMake(, , , )
blueLayer.backgroundColor = UIColor.blueColor().CGColor
blueLayer.cornerRadius = blueLayer.bounds.size.width / 2.0
blueLayer.borderColor = UIColor.purpleColor().CGColor
blueLayer.borderWidth = 10.0 blueLayer.masksToBounds = true let shadowLayer = CALayer()
shadowLayer.frame = blueLayer.frame
shadowLayer.cornerRadius = blueLayer.cornerRadius
shadowLayer.shadowColor = UIColor.redColor().CGColor
shadowLayer.shadowOffset = CGSizeMake( , 23.0)
shadowLayer.shadowOpacity =
shadowLayer.shadowRadius =
shadowLayer.backgroundColor = UIColor.redColor().CGColor
self.view.layer.addSublayer(shadowLayer) self.view.layer.addSublayer(blueLayer)

  需要注意的是阴影层需要有背景色,不然它的阴影显示不出来

  shadowPath属性可以设置阴影的形状,注意swift中的CGMutablePath并不会增加引用计数,你不需要relase它也没有方法可以提供给你release

  let  circlePath = CGPathCreateMutable()
CGPathAddEllipseInRect(circlePath, nil, self.blueLayer.bounds)
shadowLayer.shadowPath = circlePath

  当然你也可以使用UIBezierPath来设置更复杂的形状给shadowPath

 let path1 = UIBezierPath(roundedRect: CGRectMake(-, -, , ), cornerRadius: ).CGPath
let path2 = UIBezierPath(arcCenter: CGPointMake(, ), radius: , startAngle: , endAngle: 3.14159265, clockwise: true).CGPath
shadowLayer.shadowPath = path1
shadowLayer.shadowPath = path2

  图片蒙板

  layer的mask属性可以设置一个layer给他,这个layer的contents应该是一个32位有alpha通道的png图片,你可以设置一个不规则的图片其它部分为透明,这样就对layer设置了一个蒙板,蒙板的颜色不重要,轮廓比较重要,最后被设置了蒙板的layer它只会显示mask蒙板形状的内容.

 let maskLayer = CALayer()
maskLayer.frame = CGRectMake(, , , )
maskLayer.contents = UIImage(named: "111.png")?.CGImage
blueLayer.mask = maskLayer

  拉伸

  如果设置的图片不需要拉伸有很多好处,即不需要拉伸图片,又能合理的使用内存和cpu,但是很多时候一张图片要多个位置使用,所以需要拉伸,iOS跟我们提供了3中拉伸方式kCAFilterLinear/kCAFilterNearest/kCAFilterTrilinear,设置拉伸方式只需要跟layer的这两个属性赋对应的拉伸方式就可以:minification(缩小图片)和magnification(放大图片),这个属性默认是kCAFilterLinear,它和kCAFilterTrilinear类似,他们都是线性的,意思就是它会取两个值的过度色,让对应点能顺滑的过渡,如果图片有渐变色和斜线比较多就需要用这个默认的,如果图片主要是单色而且主要是垂直方向的颜色,那么就需要kCAFilterNearest,它是暴力的直接取周围的颜色,那样又不会失真,也不会太消耗cpu

  组透明

  iOS8默认就是组透明,在应用透明度之前,它会把子图层和它整合成一个整体的图片,那样就没有透明度混合的问题了,目前没有测试出透明度出问题的情况,如果有可以设置layer的shouldRasterize的值为YES

iOS核心动画高级技巧之CALayer(一)的更多相关文章

  1. iOS核心动画高级技巧之核心动画(三)

    iOS核心动画高级技巧之CALayer(一) iOS核心动画高级技巧之图层变换和专用图层(二)iOS核心动画高级技巧之核心动画(三)iOS核心动画高级技巧之性能(四)iOS核心动画高级技巧之动画总结( ...

  2. iOS核心动画高级技巧之图层变换和专用图层(二)

    iOS核心动画高级技巧之CALayer(一) iOS核心动画高级技巧之图层变换和专用图层(二)iOS核心动画高级技巧之核心动画(三)iOS核心动画高级技巧之性能(四)iOS核心动画高级技巧之动画总结( ...

  3. iOS核心动画高级技巧 - 8

    iOS核心动画高级技巧 - 1 iOS核心动画高级技巧 - 2 iOS核心动画高级技巧 - 3 iOS核心动画高级技巧 - 4 iOS核心动画高级技巧 - 5 iOS核心动画高级技巧 - 6 iOS核 ...

  4. iOS核心动画高级技巧 - 7

    13. 高效绘图 高效绘图 不必要的效率考虑往往是性能问题的万恶之源. ——William Allan Wulf 在第12章『速度的曲率』我们学习如何用Instruments来诊断Core Anima ...

  5. iOS核心动画高级技巧 - 6

    11. 基于定时器的动画 基于定时器的动画 我可以指导你,但是你必须按照我说的做. -- 骇客帝国 在第10章“缓冲”中,我们研究了CAMediaTimingFunction,它是一个通过控制动画缓冲 ...

  6. iOS核心动画高级技巧-2

    3. 图层几何学 图层几何学 不熟悉几何学的人就不要来这里了 --柏拉图学院入口的签名 在第二章里面,我们介绍了图层背后的图片,和一些控制图层坐标和旋转的属性.在这一章中,我们将要看一看图层内部是如何 ...

  7. iOS核心动画高级技巧-1

    1. 图层树 图层的树状结构 巨妖有图层,洋葱也有图层,你有吗?我们都有图层 -- 史莱克 Core Animation其实是一个令人误解的命名.你可能认为它只是用来做动画的,但实际上它是从一个叫做L ...

  8. iOS核心动画高级技巧 - 3

    7. 隐式动画 隐式动画 按照我的意思去做,而不是我说的. -- 埃德娜,辛普森 我们在第一部分讨论了Core Animation除了动画之外可以做到的任何事情.但是动画是Core Animation ...

  9. iOS核心动画高级技巧-5

    9. 图层时间 图层时间 时间和空间最大的区别在于,时间不能被复用 -- 弗斯特梅里克 在上面两章中,我们探讨了可以用CAAnimation和它的子类实现的多种图层动画.动画的发生是需要持续一段时间的 ...

随机推荐

  1. 查询字符串中防止SQL注入

    写SQL语句时,为了防止SQL注入, 通常做如下处理 strSearch.ToLower.Replace("--", " ").Replace(" - ...

  2. 1.1 sql注入分类与详解

    1.基于报错的 SQL 盲注------构造 payload 让信息通过错误提示回显出来     这里来讲一下报错注入的原理(floor型爆错注入): 0x01:报错过程: 1.rand()用于产生一 ...

  3. 面试题18(一):在O(1)时间删除链表结点

    // 面试题18(一):在O(1)时间删除链表结点 // 题目:给定单向链表的头指针和一个结点指针,定义一个函数在O(1)时间删除该 // 结点.链表结点与函数的定义如下: // struct Lis ...

  4. idea使用的知识

    1. 如何设置,使IntelliJ IDEA智能提示忽略大小写.    很好用,在settings->Editor->General-->Code Completion里设置.2. ...

  5. Sharepoint2013商务智能学习笔记之Performancepoint service 配置(九)

    1)配置Performance Service服务 第一步,新建performance service.先在管理中心,系统设置区域点击管理服务器上的服务,确认Performance Service服务 ...

  6. Python pandas检查数据中是否有NaN的几种方法

    Python pandas: check if any value is NaN in DataFrame # 查看每一列是否有NaN: df.isnull().any(axis=0) # 查看每一行 ...

  7. 实现html页面只自动跳转一次

    function show(){ var  value= sessionStorage.getItem("flg"); if(value==null || value==undef ...

  8. Selenium2.0+TestNG+Ant+Jenkins自动化测试浅尝

    当前常用自动化测试工具 Web自动化测试工具:QTP .selenium等 性能自动化测试工具:loadrunner.jmeter等 接口自动化测试工具:SoapUI.postman等 手机自动化测试 ...

  9. Exadata Adaptive Scrubbing Schedule

    1.为什么要引入"Hard Disk Scrub and Repair"特性 在exadata的11.2.3.3.0版本中,开始引进了"Automatic Hard Di ...

  10. 原生DOM操作方法小结

    1.0   DOM结构 父节点 兄弟节点 当前节点 属性节点 子节点 兄弟节点 一般任意一个节点我们都会从父节点,子节点,以及兄弟节点的角度去考察.节点一般我们只需关注元素节点,属性节点及文本节点即可 ...