图层Layer

来源:互联网 发布:linux libc 源代码 编辑:程序博客网 时间:2024/05/24 04:23

图层Layer

1. layer不做实际的绘制,捕获app所提供的内容缓存在一个bitmap中,也可赋值layer的contents属性

Most layers do not do any actual drawing in your app. Instead, a layer captures the content your app provides and caches it in a bitmap, which is sometimes referred to as the backing store. When you subsequently change a property of the layer, all you are doing is changing the state information associated with the layer object. When a change triggers an animation, Core Animation passes the layer’s bitmap and state information to the graphics hardware, which does the work of rendering the bitmap using the new information

2. layer拥有两套坐标系:基于点的坐标系和单位坐标系,前者用于定义layer在父layer中的位置和自身的size,如position和bounds;后者用于自身坐标系,如anchorPoint,取值在0和1之间。layer也有frame属性,但其是position和bounds计算出来的,用的比较少。

2.1 anchorPoint与position 、frame关系

  1. position是layer中的anchorPoint在superLayer中的位置坐标。
  2. 互不影响原则:单独修改position与anchorPoint中任何一个属性都不影响另一个属性,影响的是frame(根据公式)。
  3. frame、position与anchorPoint有以下关系:
    frame.origin.x = position.x - anchorPoint.x * bounds.size.width;
    frame.origin.y = position.y - anchorPoint.y * bounds.size.height;
    第2条的互不影响原则还可以这样理解:position与anchorPoint是处于不同坐标空间中的重合点,修改重合点在一个坐标空间的位置不影响该重合点在另一个坐标空间中的位置。修改frame,则position变,anchorPoint不动

3. anchorPoint影响layer的position与transform,如layer的旋转是基于anchorPoint

4. 实现Core Animation的layer拥有3个系列的layer对象

  1. 本身的layer tree,代表Animation的最终状态的layer;
  2. presentation layer,代表正在running的animation的状态,即中间的某一状态;
  3. render tree,animation私有,一般不访问。
    这三个是一一对应的,如果想访问layer tree中对应的presentation layer,可用layer tree中layer的presentationLayer属性访问。

5. layer与view的关系

layer并不能替代view,实际上layer只是为view提供基础架构,让view更好得绘制与动画;另外,layer不能接受事件,不参与事件反应链。

6. UIView的layer类默认为CALayer,可以通过下面方式修改: 重写layerClass方法,一旦重写,后面就不能更改了

+ (Class) layerClass  {    return [CAMetalLayer class]; }

7. layer操作app提供给它的contents(CGImageRef类型),即需展示的内容bitmap,有以下方法为layer可以提供contents:

7.1 直接给layer的contents属性赋值,一般用于contents后面很少修改,需注意的是,这种赋值后,系统就不调drawRect了,设置view的backgroundColor也会失效,但layer的backgroundColor正常;另外采用此种方式,需要设置layer的contentsScale属性,因为layer并不知道设备的屏幕相关信息

self.layer.contents = (__bridge id _Nullable)(image.CGImage);self.layer.contentsScale = [UIScreen mainScreen].scale;

7.2 给layer设置一个代理,UIView的根layer就是这种方式(默认已经设置好了),然后让代理实现以下方法:
displayLayer:方法:此方法负责生成一个bitmap,然后将其赋值给layer的contents属性;

- (void)displayLayer:(CALayer *)theLayer {    // Check the value of some state property    if (self.displayYesImage) {    // Display the Yes image    theLayer.contents = [someHelperObject loadStateYesImage];    }    else {    // Display the No image    theLayer.contents = [someHelperObject loadStateNoImage];    }}

drawLayer:inContext:方法:此方法由Core Animation生成一个bitmap,生成一个context去draw这个bitmap,你在这个方法中需要做的就是在这个context中画你想要的图,不需要赋值给layer的contents属性。

- (void)drawLayer:(CALayer *)theLayer inContext:(CGContextRef)theContext {//加这句后,系统就不调drawRect了,原本就是这个函数在调    CGContextSetStrokeColorWithColor(theContext, [UIColor redColor].CGColor);    CGContextSetLineWidth(theContext, 3);    CGMutablePathRef path = CGPathCreateMutable();    CGPathMoveToPoint(path, NULL, 0, 50);    CGPathAddLineToPoint(path, NULL, 100, 50);    CGPathAddArc(path, NULL, 50, 50, 50, 0, M_PI, NO);    CGContextAddPath(theContext, path);    CGContextStrokePath(theContext);    NSLog(@"");    //或者直接draw图片image    CGImageRef imageRef = [UIImage imageNamed:@"D.jpg"].CGImage;    CGContextDrawImage(theContext, CGRectMake(0, 0, 100, 100), imageRef);    CGImageRelease(imageRef);}

这两个方法如果你都实现了的话,系统调用drawLayer:inContext:方法;对于一般情况下的UIView,系统已经自动配置了代理,我们需要做的只是重写drawRect:方法,如果你实现了以上两个方法的话,系统就不会调用drawRect:方法了。

7.3 实现自定义layer子类,然后重写下面方法draw它的contents:
重写display :方法:需要你给contents属性赋值;
重写drawInContext:方法:与上面的代理方法对应,你不需要给contents属性赋值,只需画图就行

-(void)drawInContext:(CGContextRef)ctx{    NSLog(@"3-drawInContext:");    NSLog(@"CGContext:%@",ctx);    //    CGContextRotateCTM(ctx, M_PI_4);    CGContextSetRGBFillColor(ctx, 135.0/255.0, 232.0/255.0, 84.0/255.0, 1);    CGContextSetRGBStrokeColor(ctx, 135.0/255.0, 232.0/255.0, 84.0/255.0, 1);    //    CGContextFillRect(ctx, CGRectMake(0, 0, 100, 100));    //    CGContextFillEllipseInRect(ctx, CGRectMake(50, 50, 100, 100));    CGContextMoveToPoint(ctx, 94.5, 33.5);    //// Star Drawing    CGContextAddLineToPoint(ctx,104.02, 47.39);    CGContextAddLineToPoint(ctx,120.18, 52.16);    CGContextAddLineToPoint(ctx,109.91, 65.51);    CGContextAddLineToPoint(ctx,110.37, 82.34);    CGContextAddLineToPoint(ctx,94.5, 76.7);    CGContextAddLineToPoint(ctx,78.63, 82.34);    CGContextAddLineToPoint(ctx,79.09, 65.51);    CGContextAddLineToPoint(ctx,68.82, 52.16);    CGContextAddLineToPoint(ctx,84.98, 47.39);    CGContextClosePath(ctx);    CGContextDrawPath(ctx, kCGPathFillStroke);}

对于后面两种方法,如果你是新建一个layer,则需要调用layer的setNeedDisplay方法,否则drawInContext:drawLayer:inContext:方法方法将不会调用;如果你是直接操作UIView的根图层,则不需要。
UIView的drawLayer:inContext:方法中会调用其drawRect:方法,在drawRect:方法中使用UIGraphicsGetCurrentContext()方法得到的上下文正是前面由Core Animation创建的上下文。

8. layer修饰属性

  1. background color:layer的修饰物,draw在layer的contents image下面,而view的background color也加在layer上,但与layer的background color无关,两个是独立共存的,view的背景在layer的上面,border的下面;
  2. border:layer的修饰物,draw在layer的contents image上面,如果layer有subLayer,则draw在subLayer之上;
  3. Corner Radius:layer的修饰物,draw一个mask,中间透明,四边角圆弧,Corner Radius会影响layer的background color、border(四个角视觉上被切掉),不影响layer的contents、view的background color,但如果layer的masksToBounds属性设为YES,后面两个也会影响;
  4. Shadows:layer的修饰物,不是subLayer,draw在layer的下边,由于shadows会超出layer的contents,如果masksToBounds设置为YES,shadows超出部分会被切掉,也就没有了阴影效果,如果还是想显示的话,可以为shadows生成一个layer,然后将其放在指定layer下面

    - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. [self addShadowLayer];//添加阴影效果 CALayer *subLayer = [[CALayer alloc] init]; subLayer.position = CGPointMake(50, 100); subLayer.bounds = CGRectMake(0, 0, 30, 30); subLayer.delegate = self; subLayer.masksToBounds = YES; subLayer.cornerRadius = 15;//画出来的就是个圈 [self.view.layer addSublayer:subLayer]; [subLayer setNeedsDisplay];///需要调,不然不走drawLayer: inContext:}- (void)addShadowLayer{ CALayer *shadowLayer = [[CALayer alloc] init]; shadowLayer.position = CGPointMake(50, 100); shadowLayer.bounds = CGRectMake(0, 0, 30, 30); shadowLayer.cornerRadius = 15; shadowLayer.shadowColor = [UIColor yellowColor].CGColor; shadowLayer.shadowOffset = CGSizeMake(3, 3); shadowLayer.shadowOpacity = 0.5; //layer要有内容才能显出阴影,如本身contents,backgroundColor,border shadowLayer.borderColor = [UIColor redColor].CGColor; shadowLayer.borderWidth = 2; //    shadowLayer.backgroundColor = [UIColor redColor].CGColor; //    shadowLayer.contents = (__bridge id _Nullable)([UIImage imageNamed:@"D.jpg"].CGImage); [self.view.layer addSublayer:shadowLayer];}- (void)drawLayer:(CALayer *)theLayer inContext:(CGContextRef)theContext { CGContextScaleCTM(theContext, 1, -1); CGContextTranslateCTM(theContext, 0, - 30); CGImageRef imageRef = [UIImage imageNamed:@"D.jpg"].CGImage; CGContextDrawImage(theContext, CGRectMake(0, 0, 100, 100), imageRef); CGImageRelease(imageRef);}

9 其他

  • CALayer 内部并没有属性,当调用属性方法时,它内部是通过运行时 resolveInstanceMethod 为对象临时添加一个方法,并把对应属性值保存到内部的一个 Dictionary 里,同时还会通知 delegate、创建动画等等,非常消耗资源。UIView 的关于显示相关的属性(比如frame/bounds/transform)等实际上都是 CALayer 属性映射来的,所以对 UIView 的这些属性进行调整时,消耗的资源要远大于一般的属性。对此你在应用中,应该尽量减少不必要的属性修改。
  • 父图层的有些属性会影响子图层,如speed、transform、opacity,父图层的speed属性会影响子图层的speed,如子图层和父图层的speed都设为2的话,那动画的时间实际就只有原来的1/4;
  • [view addSubView:subView]会将subView的图层加到view的根图层上,作为其子图层,位置则依赖于加入的顺序,但view的根图层的subLayers属性只包括subView的根图层,不包括其子图层(但会加到view的根图层上)。
0 0
原创粉丝点击