容易忽略的那些小点总结 (五) —— CALayer相关(四)

版本记录

版本号 时间
V1.0 2018.01.22

前言

在苹果的API文档中,有很多属性和方法我们用的不是很多,所以很容易忽略和出错,下面我就用这一个专题专门说一些不常用的API接口,下面开始。感兴趣的可以参考前面几篇文章。
1. 容易忽略的那些小点总结 (一) —— UIView UIViewTintAdjustmentMode相关(一)
2. 容易忽略的那些小点总结 (二) —— CALayer相关(一)
3. 容易忽略的那些小点总结 (三) —— CALayer相关(二)
4. 容易忽略的那些小点总结 (四) —— CALayer相关(三)

minificationFilterBias

先看一下API文档

/* The bias factor added when determining which levels of detail to use
 * when minifying using trilinear filtering. The default value is 0.
 * Animatable. */

在确定使用三线性滤波进行缩放时要使用哪些细节级别时添加的偏移因子。 默认值是0,可动画。

@property float minificationFilterBias;

needsDisplayOnBoundsChange

先看一下API文档

/* When true -setNeedsDisplay will automatically be called when the
 * bounds of the layer changes. Default value is NO. */

当true时,方法-setNeedsDisplay将在图层的边界改变时自动被调用。 默认值是NO。

@property BOOL needsDisplayOnBoundsChange;

可以这么理解,当其值为YES时,bounds发生改变就重回,NO就不重绘,默认值为NO。


drawsAsynchronously

一个布尔值,指示绘图命令是否在后台线程中被延迟和异步处理。下面先看一下API

/* When true, the CGContext object passed to the -drawInContext: method
 * may queue the drawing commands submitted to it, such that they will
 * be executed later (i.e. asynchronously to the execution of the
 * -drawInContext: method). This may allow the layer to complete its
 * drawing operations sooner than when executing synchronously. The
 * default value is NO. */

如果为true,则传递给-drawInContext:方法的CGContext对象可以对提交给它的绘图命令进行排队,以便稍后执行(即与执行-drawInContext:方法异步)。 这可能会使图层比同步执行时更快地完成绘图操作。 默认值是NO

@property BOOL drawsAsynchronously
  CA_AVAILABLE_STARTING (10.8, 6.0, 9.0, 2.0);

也可以这么理解

  • 当此属性设置为YES时,用于绘制图层内容的图形上下文将绘制命令排队,并在后台线程上执行它们,而不是同步执行它们。 异步执行这些命令可以提高某些应用程序的性能。 但是,在启用此功能之前,您应始终测量实际的性能优势。

  • 该属性适用于图层内容需要反复重绘的情况,此时设成true可能会改善性能,比如需要反复绘制大量粒子的粒子发射器图层CAEmitterLayer

  • 对于CATiledLayer。除了将图层再次分割成独立更新的小块(类似于脏矩形自动更新的概念),CATiledLayer还有一个有趣的特性:在多个线程中为每个小块同时调用-drawLayer:inContext:方法。这就避免了阻塞用户交互而且能够利用多核心新片来更快地绘制。只有一个小块的CATiledLayer是实现异步更新图片视图的简单方法。

  • 它与CATiledLayer使用的异步绘制并不相同。drawsAsynchronously-drawLayer:inContext:方法只会在主线程调用,但是CGContext并不等待每个绘制命令的结束。相反地,它会将命令加入队列,当方法返回时,在后台线程逐个执行真正的绘制。

  • 这种方式就是先记录绘制命令,然后在后台线程执行。为了实现这个过程,更多的事情不得不做,更多的内存开销。最后只是把一些工作从主线程移动出来。这个过程是需要权衡,测试的。


- (void)drawInContext:(CGContextRef)ctx;

先看一下API文档

/* Called via the -display method when the `contents' property is being
 * updated. Default implementation does nothing. The context may be
 * clipped to protect valid layer content. Subclasses that wish to find
 * the actual region to draw can call CGContextGetClipBoundingBox(). */

  当正在更新contents属性时,通过-display方法调用。 默认实现什么都不做。 可以剪辑
  上下文以保护有效的图层内容。 希望找到要绘制的实际区域的子类
  可以调用CGContextGetClipBoundingBox()。

- (void)drawInContext:(CGContextRef)ctx;

也可以这么理解

  • 这个方法的默认实现不会自己做任何绘图。 如果图层的代理实现了drawLayer:inContext:方法,则调用该方法来执行实际绘图。

  • 子类可以重写此方法并使用它来绘制图层的内容。 绘图时,应该在逻辑坐标空间中的点上指定所有的坐标。

  • 一般都按照下面的形式进行绘制

- (void)drawInContext:(CGContextRef)ctx
{
      UIGraphicsPushContext(ctx);
      UIBezierPath* p = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0,0,100,100)];
      [[UIColor blueColor] setFill];
      [p fill];
      UIGraphicsPopContext();
}
  • 如果我们实现了CALayer的-drawInContext:方法。我们传递了一个参数context。在context上面绘制,最后会在layer的一个缓存里面。我们也可以创建我们自己的context,比如 CGBitmapContextCreate()。这个函数返回一个context,然后我们可以传递这个context,然后在刚刚创建的这个context上面绘制。

  • 在自定义图层中绘图时只要自己编写一个类继承于CALayer然后在drawInContext:中绘图即可。同前面在代理方法绘图一样,要显示图层中绘制的内容也要调用图层的setNeedDisplay方法,否则drawInContext方法将不会调用。


- (void)renderInContext:(CGContextRef)ctx;

此方法直接从图层树中渲染,忽略添加到渲染树render tree中的任何动画。 渲染到图层的坐标空间,先看一下API文档。

/** Rendering properties and methods. **/

/* Renders the receiver and its sublayers into 'ctx'. This method
 * renders directly from the layer tree. Renders in the coordinate space
 * of the layer.
 *
 * WARNING: currently this method does not implement the full
 * CoreAnimation composition model, use with caution. */

将接收器及其子层渲染到“ctx”。 该方法直接从图层树中呈现。 渲染在图层的坐标空间。 警告:目前这个方法没有实现完整的CoreAnimation composition模型,谨慎使用。

- (void)renderInContext:(CGContextRef)ctx;

重要:此方法的OS X v10.5实现不支持整个Core Animation组合模型。 不渲染QCCompositionLayerCAOpenGLLayerQTMovieLayer图层。 此外,使用3D变换的图层不会被渲染,也不绘制指定backgroundFiltersfilterscompositingFiltermask值的图层。 未来版本的macOS可能会增加对渲染这些图层和属性的支持。

  • 该方法可以用于截屏,但是要注意,尝试去截取WKWebView的图。截图的结果返回的就仅仅只是一张背景图, 显然截图失败。如果直接调用layer.renderInContext需要获取对应的Context, 但是在WKWebView中执行UIGraphicsGetCurrentContext()的返回结果是nil ,一种解决思路是使用UIView的drawViewHierarchyInRect方法去截取屏幕视图。通过直接调用WKWebView的drawViewHierarchyInRect方法(afterScreenUpdates参数必须为true), 可以成功的截取WKWebView的屏幕内容。

  • 下面看一下截屏的简单实例

25 - (IBAction)BtnClick:(UIButton *)sender 
26 {  
27     //延迟两秒保存
28     dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
29         //获取图形上下文
30         //    UIGraphicsBeginImageContext(self.view.frame.size);
31         UIGraphicsBeginImageContext(self.contentView.frame.size);
32         //将view绘制到图形上下文中
33         
34         //    [self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
35         [self.contentView.layer renderInContext:UIGraphicsGetCurrentContext()];
36      
37         
38         //将截屏保存到相册
39         UIImage *newImage=UIGraphicsGetImageFromCurrentImageContext();
40         
41         UIImageWriteToSavedPhotosAlbum(newImage,self, @selector(image:didFinishSavingWithError:contextInfo:), nil);
42     });
43 }
44 
45  - (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo
46 {
47     if (error) {
48         [MBProgressHUD showError:@"保存失败,请检查是否拥有相关的权限"];
49     }
        else {
52         [MBProgressHUD showSuccess:@"保存成功!"];
53     }
54 }

参考文档

1. Programming iOS 6
2. Yong的Blog - 这个技术博客不错
3. iOS 开发:绘制像素到屏幕

后记

本篇已结束,后面更精彩~~~

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容