美文网首页
iOS 绘图优化

iOS 绘图优化

作者: 某非著名程序员 | 来源:发表于2019-08-18 11:19 被阅读0次

  在iOS中,软件绘图通常是由Core Graphics框架完成来完成。但是,在一些必要的情况下,相比Core Animation和OpenGL,Core Graphics要慢了不少。
  软件绘图不仅效率低,还会消耗可观的内存。 CALayer 只需要一些与自己相关的内存:只有它的寄宿图会消耗一定的内存空间。即使直接赋给 contents 属性一张图片,也不需要增加额外的照片存储大小。如果相同的一张图片被多个图层作为 contents 属性,那么他们将会共用同一块内存,而不是复制内存块。
  但是一旦你实现了 CALayerDelegate 协议中的 -drawLayer:inContext: 方法或者 UIView 中的 -drawRect: 方法(其实就是前者的包装方法),图层就创建了一个绘制上下文,这个上下文需要的大小的内存可从这个算式得出:图层宽图层高4字节,宽高的单位均为像素。对于一个在Retina iPad上的全屏图层来说,这个内存量就是 204815264字节,相当于12MB内存,图层每次重绘的时候都需要重新抹掉内存然后重新分配。

1.用Core Graphics实现一个简单的绘图应用

#import "DrawingCoreGraphicsView.h"

@interface DrawingCoreGraphicsView()
@property (nonatomic, strong) UIBezierPath *path;
@end

@implementation DrawingCoreGraphicsView

- (instancetype)initWithFrame:(CGRect)frame{
    self = [super initWithFrame:frame];
    if (self) {
        [self initPath];
    }
    return self;
}

- (void)awakeFromNib{
    [super awakeFromNib];
    [self initPath];
}

- (void)initPath{
    self.path = [[UIBezierPath alloc] init];
    self.path.lineJoinStyle = kCGLineJoinRound;
    self.path.lineCapStyle = kCGLineCapRound;
    self.path.lineWidth = 5;
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    //get the starting point
    CGPoint point = [[touches anyObject] locationInView:self];
    //move the path drawing cursor to the starting point
    [self.path moveToPoint:point];
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    //get the current point
    CGPoint point = [[touches anyObject] locationInView:self];
    //add a new line segment to our path
    [self.path addLineToPoint:point];
    //redraw the view
    [self setNeedsDisplay];
}

- (void)drawRect:(CGRect)rect
{
    //draw path
    [[UIColor clearColor] setFill];
    [[UIColor redColor] setStroke];
    [self.path stroke];
}

@end
绘制帧率

  这样实现的问题在于,我们画得越多,程序就会越慢。因为每次移动手指的时候都会重绘整个贝塞尔路径( UIBezierPath ),随着路径越来越复杂,每次重绘的工作就会增加,直接导致了帧数的下降。

2.用 CAShapeLayer 重新实现绘图应用

#import "DrawingShapeLayerView.h"

@interface DrawingShapeLayerView()
@property (nonatomic, strong) UIBezierPath *path;
@end

@implementation DrawingShapeLayerView

+ (Class)layerClass
{
    //this makes our view create a CAShapeLayer
    //instead of a CALayer for its backing layer
    return [CAShapeLayer class];
}

- (instancetype)initWithFrame:(CGRect)frame{
    self = [super initWithFrame:frame];
    if (self) {
        self.path = [[UIBezierPath alloc] init];
        //configure the layer
        CAShapeLayer *shapeLayer = (CAShapeLayer *)self.layer;
        shapeLayer.strokeColor = [UIColor redColor].CGColor;
        shapeLayer.fillColor = [UIColor clearColor].CGColor;
        shapeLayer.lineJoin = kCALineJoinRound;
        shapeLayer.lineCap = kCALineCapRound;
        shapeLayer.lineWidth = 5;
    }
    return self;
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    //get the starting point
    CGPoint point = [[touches anyObject] locationInView:self];
    //move the path drawing cursor to the starting point
    [self.path moveToPoint:point];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    //get the current point
    CGPoint point = [[touches anyObject] locationInView:self];
    //add a new line segment to our path
    [self.path addLineToPoint:point];
    //update the layer with a copy of the path
    
    ((CAShapeLayer *)self.layer).path = self.path.CGPath;
}

@end
绘制帧率2

  这种比CoreGraphics稍微好点,但绘制的点多了,掉帧一样很厉害。
  为了减少不必要的绘制,Mac OS和iOS设备将会把屏幕区分为需要重绘的区域和不需要重绘的区域。那些需要重绘的部分被称作『脏区域』当一个视图被改动过了,TA可能需要重绘。但是很多情况下,只是这个视图的一部分被改变了,所以重绘整个寄宿图就太浪费了。

3.下面是使用图片来绘制一个黑板檫的功能,用-setNeedsDisplayInRect: 来减少不必要的绘制

#define BRUSH_SIZE 32

@interface DrawingBlackBoardView()
@property (nonatomic, strong) NSMutableArray *strokes;
@end

@implementation DrawingBlackBoardView

- (instancetype)initWithFrame:(CGRect)frame{
    self = [super initWithFrame:frame];
    if (self) {
        self.strokes = [NSMutableArray array];
    }
    return self;
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    //get the starting point
    CGPoint point = [[touches anyObject] locationInView:self];
    //add brush stroke
    [self addBrushStrokeAtPoint:point];
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    //get the touch point
    CGPoint point = [[touches anyObject] locationInView:self];
    //add brush stroke
    [self addBrushStrokeAtPoint:point];
}

- (void)addBrushStrokeAtPoint:(CGPoint)point
{
    //add brush stroke to array
    [self.strokes addObject:[NSValue valueWithCGPoint:point]];
    [self setNeedsDisplayInRect:[self brushRectForPoint:point]];
}

- (CGRect)brushRectForPoint:(CGPoint)point
{
    return CGRectMake(point.x - BRUSH_SIZE/2, point.y - BRUSH_SIZE/2, BRUSH_SIZE, BRUSH_SIZE);
}
     
-(void)drawRect:(CGRect)rect
{
    for (NSValue *value in self.strokes) {
        CGPoint point = [value CGPointValue];
        CGRect brushRect = [self brushRectForPoint:point];
        if (CGRectIntersectsRect(rect, brushRect)) {
            [[UIImage imageNamed:@"Chalk.png"] drawInRect:brushRect];
        }
    }
}
@end

帧率

  可以看到,即使到了最后面,帧率还能接近最大值。这种只绘制重绘区域,极大的优化了重绘功能。
总结:
1.第三种方式以极大的优化了绘制的性能,我们还可以使用异步绘制来优化,性能会更棒,这个就留读者自行思考了。
2.有任何问题欢迎留言评论。

相关文章

  • iOS 绘图优化

      在iOS中,软件绘图通常是由Core Graphics框架完成来完成。但是,在一些必要的情况下,相比Core ...

  • ISO学习之Swift 简单绘图板

    参考资料 Swift 全功能的绘图板开发Swift 绘图板功能完善以及终极优化IOS SWIFT基本画图教程 示例...

  • iOS绘图详解(链接)

    iOS绘图详解iOS绘图教程

  • 2.视图绘制

    从视图设置、缓存、像素对齐以及图层应用来优化绘图速度,使用预渲染图形避免应用程序过于膨胀 iOS主要的绘图系统有U...

  • iOS 绘图

    转自:iOS绘图—— UIBezierPath 和 Core Graphics绘图进阶请参考:绘图 前言 iOS系...

  • iOS绘图框架CoreGraphics分析

    iOS绘图框架CoreGraphics分析 iOS绘图框架CoreGraphics分析

  • iOS 绘制圆角的注意事项

    1、设置简单,性能差别不明显,简单圆角场景下推荐使用。 苹果在iOS9后优化了 cornerRadius 的绘图方...

  • iOS 性能优化

    ios性能优化(一)ios性能优化(二)ios性能优化(三)

  • iOS 性能优化内存优化学习

    iOS APP渲染性能优化iOS性能优化之内存(memory)优化iOS内存和性能优化

  • IOS的性能优化包括哪几点

    iOS性能优化总结 iOS性能优化总结。关于 iOS 性能优化梳理: 基本工具、业务优化、内存优化、卡顿优化、布局...

网友评论

      本文标题:iOS 绘图优化

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