美文网首页Core Animation程序员iOS Developer
Core Animation 第二章 寄宿图

Core Animation 第二章 寄宿图

作者: Counting_S | 来源:发表于2016-12-30 16:48 被阅读358次

    序章
    第一章

    Paste_Image.png

    contents属性


    contentsCALayer的一个属性,类型为id,但如果你用CGImage以外的对象对其赋值的话你只能得到一个空的图层。

    contents之所以是id类型书中的解释为:在macOS中,CGImageNSImage对象都可以对contents属性起作用。

    还需要注意的就是UIImageCGImage 属性实际是一个 CGImageRef的结构体,所以你需要使用__bridge来进行桥接。

    layer.contents = (__bridge id)image.CGImage;
    

    书中提到如果是MRC环境下,则不需要__bridge,不过现在MRC已经成为历史了。

    接下来你可以新建一个工程或者继续使用上一章使用的工程,在根控制器的View中添加一个空白的UIView,作为我们要使用的layerView,设置contents属性。

    - (void)viewDidLoad {
        [super viewDidLoad];
        UIImage *image = [UIImage imageNamed:@"pica"];
        self.layerView.layer.contents = (__bridge id)image.CGImage;
    }
    
    向UIView的主图层中添加contents.png

    这样我们就可以不使用UIImageView来显示图片了。顺便一提,如果你使用了UIImageView来显示图片的话,你会发现UIImageView.layercontents与你赋值的UIImageCGImage其实是同一个对象。

    对比UIImageView.png

    contentsGravity


    再说这个属性之前,我们先来对我们的皮卡丘做一点小改动,把200x200的layerView变成320x180,再来看一下,会发现我们的皮卡丘被拉伸了。

    被拉伸的皮卡丘.png

    熟悉UIKit的我们很快就能想到修改contentMode来处理图片的拉伸状态。

    view.contentMode = UIViewContentModeScaleAspectFit;
    

    而在CALayer中,这个属性叫做contentsGravityNSString类型的变量,而contentMode则是一个则是一个对象,这里也可以看出UIViewCALayer进行了封装。contentsGravity可用的NSString常量如下:

    • kCAGravityCenter
    • kCAGravityTop
    • kCAGravityBottom
    • kCAGravityLeft
    • kCAGravityRight
    • kCAGravityTopLeft
    • kCAGravityTopRight
    • kCAGravityBottomLeft
    • kCAGravityBottomRight
    • kCAGravityResize
    • kCAGravityResizeAspect
    • kCAGravityResizeAspectFill

    与contentMode一样,contentsGravity也是处理内容在图层边界的对齐方式,下面我们使用 kCAGravityResizeAspect来处理我们的layerView。

    self.layerView.layer.contentsGravity = kCAGravityResizeAspect;
    
    处理contentsGravity后的皮卡丘.png

    contentsScale


    contentsScale决定了寄宿图的像素尺寸和视图大小的比例,默认值为1.0。关于试图大小,如果你接着使用上面的例子来设置contentsScale的话,你会发现contentsScale并没有生效,因为我们已经设置了layer的边界状态。而且如果你只是想要放大图片的话,我们后面还会说到一个更方便的属性transform
    更多的时候我们真正使用到contentsScale属性是为了适应Retain屏幕。它用来判断绘制图层时应该为寄宿图创建的空间大小和需要显示的拉伸程度(如果没有设置contentsGravity的话),与UIViewcontentScaleFactor属性类似。如果contentsScale为1.0则每个点分配一个像素,为2.0则每个点分配两个像素用来绘制图片。由于contentsGravity默认值为resize,所以我们需要调整一下contentsGravity以方便我们看到实际效果。

    - (void)viewDidLoad {
        [super viewDidLoad];
        UIImage *image = [UIImage imageNamed:@"pica"];
        self.layerView.layer.contents = (__bridge id)image.CGImage;
        self.layerView.layer.contentsGravity = kCAGravityCenter;
        self.layerView.layer.contentsScale = image.scale;
    }
    
    修改过contentsScale的皮卡丘.png

    可以看到我们的皮卡丘不但被放大了,而且出现了一些像素化的情况,所以通常使用的时候应该与屏幕的scale对应一致。

    self.layerView.layer.contentsScale = [UIScreen mainScreen].scale;
    

    maskToBounds


    这个属性与UIView 的 clipsToBounds 类似,用来决定是否显示超出边界的内容,设置为YES则不会显示超出部分的内容。我们可以使用刚刚的例子来测试一下。

    - (void)viewDidLoad {
        [super viewDidLoad];
        UIImage *image = [UIImage imageNamed:@"pica"];
        self.layerView.layer.contents = (__bridge id)image.CGImage;
        self.layerView.layer.contentsGravity = kCAGravityCenter;
    //    self.layerView.layer.contentsScale = [UIScreen mainScreen].scale;
        self.layerView.layer.contentsScale = image.scale;
        self.layerView.layer.masksToBounds = YES;
    }
    
    maskToBounds设置为YES的皮卡丘.png

    contentsRect

    contentsRect属性允许我们只显示寄宿图的某一个区域。和boundsframe不同的是contentsRect的单位不是点,而是 0 到 1的一个单位。默认的contentsRect是{0,0,1,1},也就是说整张寄宿图都是可见的,如果我们制定一个小一点的矩形,那么图片就会被剪裁,这里我直接使用书中的图片来解释。

    一个自定义的 contentsRect (左)和之前显示的内容(右).png

    下面我们来做一个简单的例子,这是我们的VC:

    StoryBoard中的控制器.png

    这是我们要使用的图片:

    dribbble.png
    @interface ContentsRectController ()
    @property (strong, nonatomic) IBOutletCollection(UIView) NSArray *contentViews;
    @end
    
    @implementation ContentsRectController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        UIImage *image = [UIImage imageNamed:@"dribbble-1"];
        CGFloat spaceX = 1.0 / 3.0;
        CGFloat spaceY = 1.0 / 2.0;
        for (int i = 0; i < 3; i ++) {
            for (int j = 0; j < 2; j ++) {
                //获取collection中的view
                UIView *layerView = self.contentViews[i * 2 + j];
                layerView.layer.contents = (__bridge id)image.CGImage;
                //计算,设置contentsRect
                layerView.layer.contentsRect = CGRectMake(i * spaceX, j * spaceY, spaceX, spaceY);   
            }
        }
    }
    

    效果如下:

    contentsRect效果展示.png

    contentsCenter


    首先,contentsCenter与位置无关,他是一个CGRect类型的属性,定一个一个固定的边框和在图层上可以拉伸的范围。效果与UIImage的拉伸方法类似。例如我们将contentsCenter设置为{0.25, 0.25, 0.5, 0.5}则图片的拉伸状态如下:

    contentsCenter.png

    Custom Drawing


    contents赋值CGImage并不是设置寄宿图的唯一途径,我们也可以使用Core Graphics直接绘制寄宿图。我们可以通过继承UIView并实现-drawRect:方法来自定义绘制。当UIView检测到-drawRect:方法被调用了,就会为视图分配一个寄宿图,这个寄宿图的像素尺寸为视图大小乘以contentsScale。也就是说如果你不需要寄宿图,那么你最好不要实现这个方法,那样的话会造成CPU资源和内存的浪费。

    当视图出现在屏幕上的时候-drawRect:方法就会被调用,并且绘制的结果会被缓存起来,当开发者调用-setNeedsDisplay或者影响视图表现的属性,如bounds时,寄宿图就会被更新。

    -drawRect:是一个UIView的方法,实际是layer完成了绘制与缓存。当寄宿图需要被重绘的时候CALayer就会请求它的代理给它一个寄宿图来显示。

    - (void)displayLayer:(CALayer *)layer;
    

    如果这个方法没有被实现,CALayer就会尝试下面的方法:

    - (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx;
    

    接下来我们来实际绘制一个寄宿图吧。

    - (void)viewDidLoad {
        [super viewDidLoad];   
        CALayer *blueLayer = [CALayer layer];
        blueLayer.frame = CGRectMake(50.f, 50.f, 100.f, 100.f);
        blueLayer.backgroundColor = [UIColor blueColor].CGColor;
        blueLayer.delegate = self;
        blueLayer.contentsScale = [UIScreen mainScreen].scale;
        [self.layerView.layer addSublayer:blueLayer];
        [blueLayer display];
    }
    
    - (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {
        CGContextSetLineWidth(ctx, 10.0f);
        CGContextSetStrokeColorWithColor(ctx, [UIColor redColor].CGColor);
        CGContextStrokeEllipseInRect(ctx, layer.bounds);
    }
    
    实现CALayerDelegate来绘制图层.png

    需要注意的是:

    • 我们对blueLayer调用了display方法,这是因为CALayer不会因为被显示到屏幕上就自动重绘。
    • 我们并没有设置maskToBounds属性,但是视图依然被剪裁了,这是因为使用CALayerDelegate绘制寄宿图的时候,并没有对边界外绘制提供支持。

    总结


    本章主要讲述了 contents,寄宿图相关的属性,以及如何使用CALayerDelegate绘制寄宿图。

    相关文章

      网友评论

        本文标题:Core Animation 第二章 寄宿图

        本文链接:https://www.haomeiwen.com/subject/jtymvttx.html