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(一)的更多相关文章
- iOS核心动画高级技巧之核心动画(三)
iOS核心动画高级技巧之CALayer(一) iOS核心动画高级技巧之图层变换和专用图层(二)iOS核心动画高级技巧之核心动画(三)iOS核心动画高级技巧之性能(四)iOS核心动画高级技巧之动画总结( ...
- iOS核心动画高级技巧之图层变换和专用图层(二)
iOS核心动画高级技巧之CALayer(一) iOS核心动画高级技巧之图层变换和专用图层(二)iOS核心动画高级技巧之核心动画(三)iOS核心动画高级技巧之性能(四)iOS核心动画高级技巧之动画总结( ...
- iOS核心动画高级技巧 - 8
iOS核心动画高级技巧 - 1 iOS核心动画高级技巧 - 2 iOS核心动画高级技巧 - 3 iOS核心动画高级技巧 - 4 iOS核心动画高级技巧 - 5 iOS核心动画高级技巧 - 6 iOS核 ...
- iOS核心动画高级技巧 - 7
13. 高效绘图 高效绘图 不必要的效率考虑往往是性能问题的万恶之源. ——William Allan Wulf 在第12章『速度的曲率』我们学习如何用Instruments来诊断Core Anima ...
- iOS核心动画高级技巧 - 6
11. 基于定时器的动画 基于定时器的动画 我可以指导你,但是你必须按照我说的做. -- 骇客帝国 在第10章“缓冲”中,我们研究了CAMediaTimingFunction,它是一个通过控制动画缓冲 ...
- iOS核心动画高级技巧-2
3. 图层几何学 图层几何学 不熟悉几何学的人就不要来这里了 --柏拉图学院入口的签名 在第二章里面,我们介绍了图层背后的图片,和一些控制图层坐标和旋转的属性.在这一章中,我们将要看一看图层内部是如何 ...
- iOS核心动画高级技巧-1
1. 图层树 图层的树状结构 巨妖有图层,洋葱也有图层,你有吗?我们都有图层 -- 史莱克 Core Animation其实是一个令人误解的命名.你可能认为它只是用来做动画的,但实际上它是从一个叫做L ...
- iOS核心动画高级技巧 - 3
7. 隐式动画 隐式动画 按照我的意思去做,而不是我说的. -- 埃德娜,辛普森 我们在第一部分讨论了Core Animation除了动画之外可以做到的任何事情.但是动画是Core Animation ...
- iOS核心动画高级技巧-5
9. 图层时间 图层时间 时间和空间最大的区别在于,时间不能被复用 -- 弗斯特梅里克 在上面两章中,我们探讨了可以用CAAnimation和它的子类实现的多种图层动画.动画的发生是需要持续一段时间的 ...
随机推荐
- 洛谷P4762 [CERC2014]Virus synthesis(回文自动机+dp)
传送门 回文自动机的好题啊 先建一个回文自动机,然后记$dp[i]$表示转移到$i$节点代表的回文串的最少的需要次数 首先肯定2操作越多越好,经过2操作之后的串必定是一个回文串,所以最后的答案肯定是由 ...
- 为什么要把系统拆分成分布式的,为啥要用Dubbo?
阅读本文大概需要 6 分钟. 作者:yanglbme 1.面试题 为什么要进行系统拆分?如何进行系统拆分?拆分后不用 dubbo 可以吗? 2.面试官心里分析 从这个问题开始就进行分布式系统环节了,好 ...
- 10分钟了解什么是BFC
BFC对于已经是一个耳熟能详的词汇了,而且在前端面试中,这题也是一个高频题.虽然我们平时在开发的时候知道如何利用BFC来解决问题,但是我们要具体说出BFC的概念和怎么触发BFC,我相信很多小伙伴也是和 ...
- 不建议使用Restsharp
Restsharp确实是个优秀的插件,它最大的特点是内置了JsonConverter, 在一定程度上简化了HttpWebRequest的使用,在nuget上面有19.3M的下载量,是个很好的证明. 但 ...
- Gdiplus的使用 gdi+
使用步骤: 1.包括相应的头文件及引入相应的lib 1 #include <GdiPlus.h> 2 #pragma comment(lib, "gdiplus.lib" ...
- 禁止tableview 像上滑动
tableView有一个bounces属性.默认YES,所以tableView上下用力拉都会有弹性滑动,如下设置可以禁止,但是这样的话上下弹性都没了 而经常的需求是上方不要弹性,下方要弹性,可以用监听 ...
- Jmeter性能测试-----参数化方法CSVRead函数
Jmeter里面参数化的方法有很多,大家可以结合自己的项目情况来使用哪种方式来调用测试 数据. 下面我给大家介绍下Jmeter里CSVRead函数来获取参数的方法: 我这里已去到直播间发表评论为例(这 ...
- Navicat连接MySQL数据库的一些问题与解决方案
前言 安装MySQL数据库与Navicat并不算难事,关键是怎么让他们工作花费了我整整一天的时间,最终才把弄好.遇到各种各样的问题,上网看了大量博客,发现很多博客都是直接copy或者并不能非常好的解答 ...
- KMP 串的模式匹配 (25 分)
给定两个由英文字母组成的字符串 String 和 Pattern,要求找到 Pattern 在 String 中第一次出现的位置,并将此位置后的 String 的子串输出.如果找不到,则输出“Not ...
- 查询rabbitmq
package com.yunda.app.service; import java.io.InputStream; import java.net.HttpURLConnection; import ...