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和它的子类实现的多种图层动画.动画的发生是需要持续一段时间的 ...
随机推荐
- p2055&bzoj1433 假期的宿舍
传送门(洛谷) 传送门(bzoj) 题目 学校放假了······有些同学回家了,而有些同学则有以前的好朋友来探访,那么住宿就是一个问题.比如A 和B都是学校的学生,A要回家,而C来看B,C与A不认识. ...
- 8. CTF综合靶机渗透(一)
靶机说明 虚拟机难度中等,使用ubuntu(32位),其他软件包有: PHP apache MySQL 目标 Boot to root:从Web应用程序进入虚拟机,并获得root权限. 运行环境 靶机 ...
- 【本人译作推荐】Windows 8应用开发:C#和XAML卷(原名:Building Windows 8 Apps with C# and XAML)
[图书推荐] 译名:Windows 8应用开发:C#和XAML卷 原名:Building Windows 8 Apps with C# and XAML 编辑推荐 国内第一本使用XAML与C#语言 ...
- BKMyFAQ
邮箱配置如图 发送格式: { "bk_app_code": "bk_monitor", #该字段可以查看文件:/data/install/.app.token ...
- struts2学习笔记——第一个struts2应用配置
说实在的,随着Java学习的不断深入,特别是Java web框架部分,调bug让人很心累,但是每征服一个bug,内心的成就感也是难以言说的.第一个struts2应用的配置,我昨天折腾了快2个小时,最后 ...
- 洛谷P2119 魔法阵
P2119 魔法阵 题目描述 六十年一次的魔法战争就要开始了,大魔法师准备从附近的魔法场中汲取魔法能量. 大魔法师有m个魔法物品,编号分别为1,2,...,m.每个物品具有一个魔法值,我们用Xi表示编 ...
- loj#6053. 简单的函数(Min_25筛)
传送门 题解 \(Min\_25\)筛有毒啊--肝了一个下午才看懂是个什么东西-- \(zsy\)巨巨强无敌-- //minamoto #include<bits/stdc++.h> #d ...
- 解读人:朱月琴,Hippocampal proteomic alteration in triple transgenic mouse model of Alzheimer’s disease and implication of PINK 1 regulation in donepezil treatment
文章中文名:阿尔茨海默病三联转基因小鼠模型的海马蛋白质组学改变及Donepezil治疗中PINK 1调节的意义 发表时间:(2019年4月) IF:3.95 单位:澳门大学,威斯康星大学,暨南大学,广 ...
- Spring Boot整合实战Spring Security JWT权限鉴权系统
目前流行的前后端分离让Java程序员可以更加专注的做好后台业务逻辑的功能实现,提供如返回Json格式的数据接口就可以.像以前做项目的安全认证基于 session 的登录拦截,属于后端全栈式的开发的模式 ...
- CentOS编译安装GCC 4.9.2成功
在Linux上编译安装gcc是个寻烦恼的活,对于像我这样习惯于在Windows上面使用二进制安装包的人来说,自已编译安装gcc是个相当大的挑战,今天直接挑战最新版的gcc,是4.9.2版本的,做之前查 ...