iOS里面一个View的可见是因为内部的一个CALayer类型的layer属性,能够响应事件是因为继承了UIResponder类。当UIView需要显示在屏幕上的时候会调用drawRect:方法进行
绘图,绘图完毕后系统会将图层拷贝到屏幕上,完成显示。
通过操作CALayer对象可以很方便的操作UIView的一些外观属性,还可以给图层添加动画来实现一些比较炫酷的效果。
常用的属性包含以下:@property CGRect bounds;
/* 宽高 The position in the superlayer that the anchor point of the layer's * bounds rect is aligned to. Defaults to the zero point. Animatable. */ @property CGPoint position; /* 中心位置,The Z component of the layer's position in its superlayer. Defaults * to zero. Animatable. */ @property CGPoint anchorPoint; /*
这个属性比较重要,锚点决定了那layer的某个点出现在position的位置。
The Z component of the layer's anchor point (i.e. reference point for * position and transform). Defaults to zero. Animatable. */ @property(nullable) CGColorRef backgroundColor;
/* 背景颜色
When positive, the background of the layer will be drawn with * rounded corners. Also effects the mask generated by the * `masksToBounds' property. Defaults to zero. Animatable. */ @property CGFloat cornerRadius; /*
切圆角
The width of the layer's border, inset from the layer bounds. The * border is composited above the layer's content and sublayers and * includes the effects of the `cornerRadius' property. Defaults to * zero. Animatable. */ @property CGFloat borderWidth; /* The color of the layer's border. Defaults to opaque black. Colors * created from tiled patterns are supported. Animatable. */ @property(nullable) CGColorRef borderColor; /* The opacity of the layer, as a value between zero and one. Defaults * to one. Specifying a value outside the [0,1] range will give undefined * results. Animatable. */
当设置某些控件发现圆角不起作用的时候要考虑,控件不仅仅有一个layer,显示内容不是在主层上面,应该考虑 self.myView.layer.masksToBounds设置为YES,使得超过主层的部分给清除,但是操作这里的时候阴影自然也就被清除了,如果要保证既有阴影,又无压力的切圆角,可以采用其他措施,比如可以用Quartz 2D方法生成一个圆角的图片然后再用。
layer支持3D效果的旋转,第一个参数表示旋转的角度,后面3个是x,y,z的坐标,表示沿着从原点到坐标(x,y,z)所在的直线的轴进行旋转,如果依然想沿着手机屏幕旋转只需要沿着z轴旋转就可以了,
layer的创建
1.直接使用CALayer创建
CALayer *layer = [CALayer layer]; layer.backgroundColor = [UIColor redColor].CGColor; layer.bounds = CGRectMake(0, 0, 100, 100); layer.position = CGPointMake(200, 100); layer.cornerRadius = 10; layer.masksToBounds = YES; //这个属性是id类型,可以添加的不仅仅是image layer.contents = (id)[UIImage imageNamed:@"one"].CGImage; [self.view.layer addSublayer:layer];
上面这种创建的layer无需强制调用setNeedsDisplay方法就可以显示。
@property(nullable, weak) id delegate;
这是layer的代理的定义,是一个id类型的,表示任何类都可以当成他的代理,而且任何类都已经存在他的代理方法。只要在代理类重写下面方法后,然后layer再调用
setNeedsDisplay,就可以自动调用下面的方法实现绘图功能,
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx
2.自己定义的layer继承CALayer。
这个时候要在自定义的layer类里面在下面方法添加代码
- (void)drawInContext:(CGContextRef)ctx
的ctx参数就是当前图层的上下文,在UIView里面的drawRect 方法里面常常用来绘制当前view的图层,需要下面代码来获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
通过自定义继承的图层使用的时候,
CALayer的隐式动画
每个UIView内部都有一个CALayer,所有的手动创建的CALayer对象(非创建View的时候带的layer),当对他的部分属性进行修改的时候默认会产生一些动画效果,这些属性称为
// [CATransaction begin]; // 开启事务 // [CATransaction setDisableActions:YES]; //这里修改会触发隐式动画的属性将不再触发隐式动画 // [CATransaction commit]; // 提交事务
Core Animation(核心动画)
除了隐式动画外,CALayer可以手动的添加动画,动画是一个类,只要要创建一个动画,然后设置该动画的属性,把动画添加到图层上就能实现动画效果
CALayer中很多属性都可以通过CAAnimation实现动画效果,包括:opacity、position、transform、bounds、contents等(可以在API文档中搜索:CALayer Animatable Properties) 通过调用CALayer的addAnimation:forKey增加动画到层(CALayer)中,这样就能触发动画了。通过调用removeAnimationForKey可以停止层中的动画 Core Animation的动画执行过程都是在后台操作的,不会阻塞主线程。
一共有4种常用的动画类,继承了CAAnimation,CAAnimation是抽象类,不能直接使用,继承结构如下:
所有动画对象的父类,负责控制动画的持续时间和速度,是个抽象类,不能直接使用,应该使用它具体的子类
属性解析:(红色代表来自CAMediaTiming协议的属性)
duration:动画的持续时间
repeatCount:动画的重复次数
repeatDuration:动画的重复时间
removedOnCompletion:默认为YES,代表动画执行完毕后就从图层上移除,图形会恢复到动画执行前的状态。如果想让图层保持显示动画执行后的状态,那就设置为NO,不过还要设置fillMode为kCAFillModeForwards
fillMode:决定当前对象在非active时间段的行为.比如动画开始之前,动画结束之后
beginTime:可以用来设置动画延迟执行时间,若想延迟2s,就设置为CACurrentMediaTime()+2,CACurrentMediaTime()为图层的当前时间
timingFunction:速度控制函数,控制动画运行的节奏
delegate:动画代理
主要的步骤就是:
初始化一个动画对象,并设置一些动画的相关属性,添加动画到层中,开始执行动画。
CABasicAnimation
//移动 - (void)testTransform { // 1.创建动画对象 CABasicAnimation *anim = [CABasicAnimation animation]; // 2.设置动画对象 // keyPath决定了执行怎样的动画, 调整哪个属性来执行动画 // anim.keyPath = @"transform.rotation"; // anim.keyPath = @"transform.scale.x"; anim.keyPath = @"transform.translation.x"; anim.toValue = @(100); // anim.toValue = [NSValue valueWithCGPoint:CGPointMake(100, 100)]; anim.duration = 2.0; anim.removedOnCompletion = NO; anim.fillMode = kCAFillModeForwards; // 3.添加动画 [self.layer addAnimation:anim forKey:nil]; } //旋转 - (void)testRotate { // 1.创建动画对象 CABasicAnimation *anim = [CABasicAnimation animation]; // 2.设置动画对象 // keyPath决定了执行怎样的动画, 调整哪个属性来执行动画 anim.keyPath = @"transform"; // anim.fromValue = [NSValue valueWithCGPoint:CGPointMake(0, 0)]; anim.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(M_PI, 1, -1, 0)]; anim.duration = 2.0; anim.removedOnCompletion = NO; anim.fillMode = kCAFillModeForwards; // 3.添加动画 [self.layer addAnimation:anim forKey:nil]; } //缩放 - (void)testScale { // 1.创建动画对象 CABasicAnimation *anim = [CABasicAnimation animation]; // 2.设置动画对象 // keyPath决定了执行怎样的动画, 调整哪个属性来执行动画 anim.keyPath = @"bounds"; // anim.fromValue = [NSValue valueWithCGPoint:CGPointMake(0, 0)]; anim.toValue = [NSValue valueWithCGRect:CGRectMake(0, 0, 200, 200)]; anim.duration = 2.0; /**让图层保持动画执行完毕后的状态**/ // 动画执行完毕后不要删除动画 anim.removedOnCompletion = NO; // 保持最新的状态 anim.fillMode = kCAFillModeForwards; // 3.添加动画 [self.layer addAnimation:anim forKey:nil]; } //移动 - (void)testTranslate { // 1.创建动画对象 CABasicAnimation *anim = [CABasicAnimation animation]; // 2.设置动画对象 // keyPath决定了执行怎样的动画, 调整哪个属性来执行动画 anim.keyPath = @"position"; // anim.fromValue = [NSValue valueWithCGPoint:CGPointMake(0, 0)]; // toValue : 最终变成什么值 // byValue : 增加多少值 anim.byValue = [NSValue valueWithCGPoint:CGPointMake(200, 200)]; anim.duration = 2.0; /**让图层保持动画执行完毕后的状态**/ // 动画执行完毕后不要删除动画 anim.removedOnCompletion = NO; // 保持最新的状态 anim.fillMode = kCAFillModeForwards; // 3.添加动画 [self.layer addAnimation:anim forKey:nil]; }
CAKeyframeAnimation
CApropertyAnimation的子类,跟CABasicAnimation的区别是:CABasic Animation只能从一个数值(fromValue)变到另一个数值(toValue),CAKeyframeAnimation会使用一个NSArray保存这些数值。
values:就是上述的NSArray对象。里面的元素称为”关键帧”(keyframe)。动画对象会在指定的时间(duration)内,依次显示values数组中的每一个关键帧
path:可以设置一个CGPathRef\CGMutablePathRef,让层跟着路径移动。path只对CALayer的anchorPoint和position起作用。如果你设置了path,那么values将被忽略
keyTimes:可以为对应的关键帧指定对应的时间点,其取值范围为0到1.0,keyTimes中的每一个时间值都对应values中的每一帧.当keyTimes没有设置的时候,各个关键帧的时间是
平分的 CABasicAnimation可看做是最多只有2个关键帧的CAKeyframeAnimation
//抖动效果 - (void)shake { CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation"]; float angle = angle2radian(3); anim.values = @[@(-angle), @(angle), @(-angle)]; anim.duration = 0.2; anim.repeatCount = MAXFLOAT; [self.myview.layer addAnimation:anim forKey:nil]; } //移动 - (void)translate2 { CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:@"position"]; CGMutablePathRef path = CGPathCreateMutable(); CGPathAddEllipseInRect(path, NULL, CGRectMake(0, 0, 300, 300)); anim.duration = 2; anim.path = path; [self.myview.layer addAnimation:anim forKey:nil]; CGPathRelease(path); } //移动 - (void)translate { CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:@"position"]; // 设置动画执行的一个节奏 anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]; anim.duration = 2; NSValue *p1 = [NSValue valueWithCGPoint:CGPointMake(0, 0)]; NSValue *p2 = [NSValue valueWithCGPoint:CGPointMake(300, 0)]; NSValue *p3 = [NSValue valueWithCGPoint:CGPointMake(300, 300)]; NSValue *p4 = [NSValue valueWithCGPoint:CGPointMake(0, 300)]; anim.values = @[p1, p2, p3, p4]; [self.myview.layer addAnimation:anim forKey:nil]; }
CATransition
用于做转场动画,能够为层提供移出屏幕和移入屏幕的动画效果
type:动画过渡类型
subtype:动画过渡方向
startProgress:动画起点(在整体动画的百分比)
endProgress:动画终点(在整体动画的百分比)
CATransition *anim = [CATransition animation]; // 动画类型 anim.type = @"pageCurl"; // 过渡方向 anim.subtype = kCATransitionFromRight; anim.duration = 0.5; [self.imageView.layer addAnimation:anim forKey:nil];
type的意义
- pageCurl 向上翻一页
- pageUnCurl 向下翻一页
- rippleEffect 滴水效果
- suckEffect 收缩效果,如一块布被抽走
- cube 立方体效果
- oglFlip 上下翻转效果