iOS核心动画高级技巧(上)

说明:此文仅为笔记,笔者自己总结了一些重点,查阅原文请戳这里: iOS核心动画高级技巧

视觉效果

拉伸过滤

CALayer提供了三种拉伸过滤的方法:

  • kCAFilterLinear
  • kCAFilterNearest
  • kCAFilterTrilinear

线性过滤保留了形状,最近过滤则保留了像素的差异。所以,对于比较小的图或者是差异特别明显、极少斜线的大图,使用kCAFilterNearest;而对于大多数图尤其是有很多斜线或者曲线轮廓的图片来说,使用线性过滤。kCAFilterLinear是默认的方式。

组透明

UIView有一个叫做alpha的属性来确定视图的透明度,CALayer有个同等的属性叫做opacity。给控件设置透明度的时候,如果该控件有子视图的时候就会出现奇怪的效果。

为了达成整体一样的透明效果,可以通过设置Info.plist文件中的UIViewGroupOpacity为YES来达到这个效果,但是这个设置会影响到这个应用,整个app可能会受到不良影响。所以,这就要使用一个更好的解决办法,可以设置CALayer的一个叫做shouldRasterize属性来实现组透明的效果,把它设置为YES

注意:如果使用了shouldRasterize属性,你就要确保设置了rasterizationScale属性去匹配屏幕,以防止出现Retina屏幕像素化的问题。

变换

仿射变换

基础变换可以用以下几个函数创建CGAffineTransform实例:

  • CGAffineTransformMakeRotation(CGFloat angle)
  • CGAffineTransformMakeScale(CGFloat sx, CGFloat sy)
  • CGAffineTransformMakeTranslation(CGFloat tx, CGFloat ty)

M_PI_4表示1/4的Pi,下面的宏可以做弧度换算:

#define RADIANS_TO_DEGREES(x) ((x)/180.0*M_PI)
混合变换

深层次的变换,可以使用如下函数:

  • CGAffineTransformRotate(CGAffineTransform t, CGFloat angle)
  • CGAffineTransformScale(CGAffineTransform t, CGFloat sx, CGFloat sy)
  • CGAffineTransformTranslate(CGAffineTransform t, CGFloat tx, CGFloat ty)

当操纵一个变换的时候,创建一个CGAffineTransform类型的空值很重要,Core Graphics提供了一个方便的常量:CGAffineTransformIdentity。如果需要混合两个已经存在的变换矩阵,使用如下方法,在两个变换的基础上创建一个新的变换:

CGAffineTransformConcat(CGAffineTransform t1, CGAffineTransform t2);
3D变换

3D变换常用如下函数:

  • CATransform3DMakeRotation(CGFloat angle, CGFloat x, CGFloat y, CGFloat z)
  • CATransform3DMakeScale(CGFloat sx, CGFloat sy, CGFloat sz)
  • CATransform3DMakeTranslation(Gloat tx, CGFloat ty, CGFloat tz)

3D变换中的透视投影:可以使用CATransform3Dm34元素来实现,其默认值为0,我们可以通过设置m34为-1.0/d来应用透视效果,d代表了想象中视角相机和屏幕之间的距离,以像素为单位,一般取500~1000就很好了。

灭点:当在透视角度绘图的时候,远离相机视角的物体将会变小变远,当远离到一个极限距离,它们可能就缩成了一个点,于是所有的物体最后都汇聚消失在同一个点。

5.11.jpeg

灭点


Core Animation定义了这个点位于变换图层的anchorPoint。这就是说,当图层发生变换时,这个点永远位于图层变换之前anchorPoint的位置。

sublayerTransform属性:

var perspective=CATransform3DIdentity
perspective.m34 = -1.0 / 500.0
self.containerView.layer.sublayerTransform = perspective;//sublayerTransform属性的应用
let transform1=CATransform3DMakeRotation(CGFloat(M_PI_4), 0, 1, 0);
self.layerView1.layer.transform=transform1
let transform2=CATransform3DMakeRotation(CGFloat(-M_PI_4), 0, 1, 0);
self.layerView2.layer.transform=transform2

达成的效果:

1240

transform.png


sublayerTransform也是CATransform3D类型,使用它将影响所有的子图层。这就意味着你可以一次性对包含这些图层的容器做变换,于是所有的子图层都自动继承了这个变换方法。
背面:图层是双面绘制的,反面显示的是正面的一个镜像图片。CALayer有一个叫做doubleSided的属性来控制图层的背面是否要被绘制。

专用图层

CAShapeLayer

我们可以使用CAShapeLayer构造不同形状的图层,它是通过矢量图形而不是bitmap来绘制图层子类的。指定如颜色和线宽等属性,用CGPath来定义想要绘制的图层,最后用CAShapeLayer自动渲染出来。与CALayer相比其优势是:

  • 渲染快速:使用了硬件加速
  • 高效使用内存:不需要像普通的CALayer一样创建一个寄宿图形,所以不会占用太多内存
  • 不会被图层边界剪裁掉:一个CAShapeLayer可以在边界之外绘制
  • 不会出现像素化:不会像普通图层一样变得像素化

CAShapeLayer属性是CGPathRef类型,但是我们用UIBezierPath帮助类创建了图层路径,这样我们就不用考虑人工释放CGPath了。在绘制的过程中,绘制的形状不一定要闭合,图层路径也不一定要不可破,事实上可以在一个图层上绘制好几个不同的形状。可以控制一些属性比如:linewidth(线宽)、lineCap(线结尾的样子)、lineJoin(线条间结合点的样子),但是在图层层面只有一次机会设置这些属性。

let shapeLayer=CAShapeLayer()
shapeLayer.strokeColor=UIColor.redColor().CGColor
shapeLayer.fillColor=UIColor.clearColor().CGColor
shapeLayer.lineWidth=5
shapeLayer.lineJoin=kCALineJoinRound
shapeLayer.lineCap=kCALineCapRound
shapeLayer.path=path.CGPath

圆角:CAShapeLayer有一个优势,即可以单独指定每个角。

let rect=CGRectMake(50, 50, 100, 100) 
let radii=CGSizeMake(20, 20) 
let corners=UIRectCorner.BottomLeft//该属性指定哪个角为圆角  
let path=UIBezierPath.init(roundedRect: rect,byRoundingCorners: corners, cornerRadii: radii) 
shapeLayer.path=path.CGPath
CATextLayer

CATextLayer以图层的形式包含了UILabel几乎所有的绘制特性,并额外提供了一些新的特性,而且CATextLayer使用了Core Text,比UILabel渲染要快得多。

let textLayer=CATextLayer()
textLayer.frame=self.containerView.bounds 
self.containerView.layer.addSublayer(textLayer)

textLayer.foregroundColor=UIColor.blackColor().CGColor 
textLayer.alignmentMode=kCAAlignmentJustified
textLayer.wrapped = true  

let font=UIFont.systemFontOfSize(15)  
let fontName=font.fontName 
let fontRef=CGFontCreateWithFontName(fontName) 
textLayer.font=fontRef 
textLayer.fontSize=font.pointSize

let text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque massa arcu, eleifend vel varius in, facilisis pulvinar leo. Nunc quis nunc at mauris pharetra condimentum ut ac neque. Nunc elementum, libero ut porttitor dictum, diam odio congue lacus, vel fringilla sapien diam at purus. Etiam suscipit pretium nunc sit amet lobortis"
textLayer.string=text
textLayer.contentsScale=UIScreen.mainScreen().scale//contentScale属性用来决定图层内容应该以怎样的分辨率来渲

注:CATextLayer的string属性并不是你想象的NSString类型,而是id类型。这样你既可以用NSString也可以用NSAttributedString来指定文本了。

富文本:

let string=NSMutableAttributedString.init(string: text)
var attribs: Dictionary<String, AnyObject> = [kCTForegroundColorAttributeName as String: UIColor.blackColor().CGColor,
kCTFontAttributeName as String: fontRef]
string.setAttributes(attires,range: NSMakeRange(0, text.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)))
attribs = [kCTForegroundColorAttributeName as String: UIColor.redColor().CGColor,
kCTUnderlineStyleAttributeName as String: NSNumber(int: CTUnderlineStyle.Single.rawValue),
kCTFontAttributeName as String: fontRef ]
string.setAttributes(attribs, range: NSMakeRange(6, 5))

以上的swift代码在创建attribs字典的时候不知道有没有问题,运行之后显示的结果是正确的,留待以后考证。

UILabel的代替品:创建UILabel子类LayerLabel用于使用CATextLayer而不是像正常UILabel样用慢速的-drawRect:方法来绘制文本的。

下面这段oc代码转swift记录一下:

+ (Class)layerClass{
 return [CATextLayer class];
}
- (CATextLayer *)textLayer{
 return (CATextLayer *)self.layer;
}

swift:

var layerClass : AnyClass {
 get{
 return CATextLayer.classForCoder() 
} 
}
var textLayer : CATextLayer{
 get{ 
return self.layer as!CATextLayer 
} 
}

总得来说,如果想在app里面充分利用CALayer子类,用+layerClass来创建基于不同图层的视图是一个简单可复用的方法。

CATransformLayer

CATransformLayer不同于普通的CALayer,因为它不能显示它自己的内容。只有当存在了一个能作用域子图层的变换它才真正存在。CATransformLayer并不平面化它的子图层,所以它能够用于构造一个层级的3D结构

CAGradientLayer

CAGradientLayer是用来生成两种或更多颜色平滑渐变的。用Core Graphics复制一个CAGradientLayer并将内容绘制到一个普通图层的寄宿图也是有可能的,但是CAGradientLayer的真正好处在于绘制使用了硬件加速。

基础渐变: CAGradientLayerstartPointendPoint属性,他们决定了渐变的方向。

let gradientlayer=CAGradientLayer()
gradientlayer.frame=self.containerView.bounds
self.containerView.layer.addSublayer(gradientlayer) 
//设置渐变颜色
gradientlayer.colors=[UIColor.redColor().CGColor,UIColor.blueColor().CGColor] 
//设置渐变的起点和终点
gradientlayer.startPoint=CGPointMake(0, 0)gradientlayer.endPoint=CGPointMake(1, 1)

多重渐变:默认情况下,颜色在空间上均匀地被渲染,但是我们可以用locations属性来调整空间,locations属性是一个浮点数值的数组(以NSNumber包装)。

//设置locationsgradientlayer.locations=[0.0,0.25,0.5]
CAReplicatorLayer

CAReplicatorLayer的目的是为了高效生成许多相似的图层。它会绘制一个或多个图层的子图层,并在每个复制体上应用不同的变换。 其中instanceCount属性指定了图层需要重复多少次;instanceTransform指定了一个CATransform3D 3D变换
:变换是逐步增加的,每个实例都是相对于前一个实例布局。

let replicator=CAReplicatorLayer()
replicator.frame=self.containerView.bounds
self.containerView.layer.addSublayer(replicator) 
replicator.instanceCount=10 
var transform:CATransform3D=CATransform3DIdentity
transform=CATransform3DTranslate(transform, 0, 200, 0)
transform=CATransform3DRotate(transform, CGFloat(M_PI/5.0), 0, 0, 1)
transform=CATransform3DTranslate(transform, 0, -200, 0)
replicator.instanceTransform=transform 
replicator.instanceBlueOffset = -0.1
replicator.instanceGreenOffset = -0.1

注意到当图层在重复的时候,他们的颜色也在变化:这是用instanceBlueOffsetinstanceGreenOffset属性实现的。通过逐步减少蓝色和绿色通道,我们逐渐将图层颜色转换成了红色。

反射:使用CAReplicatorLayer并应用一个负比例变换于一个复制图层

var layerClass : AnyClass { 
get{ 
return CAReplicatorLayer.classForCoder()
 } 
} 
func setUp(){
let layer:CAReplicatorLayer=CAReplicatorLayer(layer: self.layer) 
layer.instanceCount=2  
var transform:CATransform3D=CATransform3DIdentity 
let verticalOffset:CGFloat=self.bounds.size.height + 40 
transform=CATransform3DTranslate(transform, 0, verticalOffset, 0)//移到下面 
transform=CATransform3DScale(transform, 1, -1, 0)//垂直翻转 
layer.instanceTransform=transform  //减少reflection layer的透明度 
layer.instanceAlphaOffset = 0.6 
}  
override init(frame: CGRect) { 
super.init(frame: frame) self.setUp()
 }
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder) self.setUp() 
} 
override func awakeFromNib() { 
super.awakeFromNib() self.setUp() 
}
CAScrollLayer

关于图层滑动,如果你的图层包含子图层,这个时候就需要CAScrollLayer了。CAScrollLayer有一个-scrollToPoint:方法,它自动适应bounds的原点以便图层内容出现在滑动的地方。

CATiledlayer

CATiledLayer为载入大图造成的性能问题提供了一个解决方案:将大图分解成小片然后将他们单独按需载入

CAEmitterLayer

CAEmitterLayer是一个高性能的粒子引擎,被用来创建实时粒子动画如:烟雾,火,雨等等这些效果。一个CAEmitterCell类似于一个CALayer:它有一个contents属性可以定义为一个CGImage,另外还有一些可设置属性控制着表现和行为。

CAEMitterCell的属性基本上可以分为三种:

  • 这种粒子的某一属性的初始值;
  • 粒子某一属性的变化范围;
  • 指定值在时间线上的变化。

birthRatelifetimecelocity,preservesDepthrenderMode

CAEAGLLayer

在iOS 5中,苹果引入了一个新的框架叫做GLKit,它去掉了一些设置OpenGL的复杂性,提供了一个叫做CLKView的UIView的子类,帮你处理大部分的设置和绘制工作。前提是各种各样的OpenGL绘图缓冲的底层可配置项仍然需要你用CAEAGLLayer完成,它是CALayer的一个子类,用来显示任意的OpenGL图形。

AVPlayerLayer

AVPlayerLayer是用来在iOS上播放视频的。他是高级接口例如MPMoivePlayer的底层实现,提供了显示视频的底层控制。AVPlayerLayer的使用相当简单:你可以用+playerLayerWithPlayer:方法创建一个已经绑定了视频播放器的图层,或者你可以先创建一个图层,然后用player属性绑定一个AVPlayer实例。

转载于:https://my.oschina.net/hejunbinlan/blog/700245

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值