iOS 画板 (OC和Swift)

Objective-C执行-(void)drawOtherImage耗时约0.03s,
Swift执行drawOtherImage()耗时约0.006s。

1. 画板使用Objective-C语言

@interface DrawBoard : UIImageView

@end

#import "DrawBoard.h"
@protocol PaintBrush
- (void)drawInContent:(CGContextRef)content;
@end

@interface BaseBrush : NSObject <PaintBrush>
@property (nonatomic, assign) CGPoint startPoint;
@property (nonatomic, assign) CGPoint endPoint;
@property (nonatomic, assign) CGPoint lastPoint;
@property (nonatomic, assign) CGFloat strokeWidth;
- (void)drawInContent:(CGContextRef)content;
@end

@implementation BaseBrush
- (void)drawInContent:(CGContextRef)content{
    NSAssert(NO, @"need to implement the method in subclass");
}
@end

@interface Pencil : BaseBrush

@end

@implementation Pencil
- (void)drawInContent:(CGContextRef)content{
    if(self.lastPoint.x && self.lastPoint.y){
        CGContextMoveToPoint(content, self.lastPoint.x, self.lastPoint.y);
        CGContextAddLineToPoint(content, self.endPoint.x, self.endPoint.y);
    }else{
        CGContextMoveToPoint(content, self.startPoint.x, self.startPoint.y);
        CGContextAddLineToPoint(content, self.endPoint.x, self.endPoint.y);
    }
}
@end


typedef NS_ENUM(NSInteger, DrawStatus) {
    DrawStatusNone,
    DrawStatusBegan,
    DrawStatusMoved,
    DrawStatusEnded
};

@interface DrawBoard()
@property (nonatomic, assign) DrawStatus drawStatus;
@property (nonatomic, strong) BaseBrush  *brush;
@property (nonatomic, strong) UIColor    *strokeColor;
@property (nonatomic, assign) CGFloat    strokeWidth;

@property (nonatomic, strong) NSMutableArray *sendPoints;
@property (nonatomic, strong) UIImage    *realImage;
@end

@implementation DrawBoard
- (instancetype)initWithFrame:(CGRect)frame{
    self = [super initWithFrame:frame];
    if(self){
        self.drawStatus = DrawStatusNone;
        self.strokeWidth = 2;
        self.strokeColor = [UIColor blackColor];
        self.brush = [[Pencil alloc] init];
        self.sendPoints = [NSMutableArray array];
    }
    return self;
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    if(self.brush){
        self.drawStatus = DrawStatusBegan;
        self.brush.lastPoint = CGPointZero;
        self.brush.startPoint = [touches.anyObject locationInView:self];
        self.brush.endPoint = [touches.anyObject locationInView:self];
        [self.sendPoints addObject:[NSValue valueWithCGPoint:self.brush.startPoint]];
//        [self drawImage];
    }
}

- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    if(self.brush){
        self.drawStatus = DrawStatusMoved;
        self.brush.endPoint = [touches.anyObject locationInView:self];
        [self.sendPoints addObject:[NSValue valueWithCGPoint:self.brush.endPoint]];
//        [self drawImage];
    }
}

- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    if(self.brush){
        self.drawStatus = DrawStatusEnded;
        self.brush.endPoint = [touches.anyObject locationInView:self];
        [self.sendPoints addObject:[NSValue valueWithCGPoint:self.brush.endPoint]];
//        [self drawImage];
        // 发送消息
        [self drawOtherImage];
    }
}

- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    if(self.brush){
        self.drawStatus = DrawStatusNone;
        self.brush.endPoint = CGPointZero;
        // 发送消息
        [self drawOtherImage];
    }
}

// 画自己的线
- (void)drawImage{
    /*
     UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, [UIScreen mainScreen].scale);

     CGContextRef content = UIGraphicsGetCurrentContext();
     [[UIColor clearColor] setFill];
     CGContextFillRect(content, self.bounds);
     CGContextSetStrokeColorWithColor(content, _strokeColor.CGColor);
     CGContextSetLineCap(content, kCGLineCapRound);
     CGContextSetLineWidth(content, _strokeWidth);
     if(_realImage){
     [_realImage drawInRect:self.bounds];
     }
     self.brush.strokeWidth = self.strokeWidth;
     [self.brush drawInContent:content];
     CGContextStrokePath(content);

     UIImage *preImage = UIGraphicsGetImageFromCurrentImageContext();
     self.realImage = preImage;
     UIGraphicsEndImageContext();

     self.image = preImage;
     self.brush.lastPoint = self.brush.endPoint;
     */
}

// 画发过来的线
- (void)drawOtherImage{
    NSLog(@"start: %f",[[NSDate date] timeIntervalSince1970]);
    UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, [UIScreen mainScreen].scale);

    CGContextRef content = UIGraphicsGetCurrentContext();
    [[UIColor clearColor] setFill];
    CGContextFillRect(content, self.bounds);
    CGContextSetStrokeColorWithColor(content, _strokeColor.CGColor);
    CGContextSetLineCap(content, kCGLineCapRound);
    CGContextSetLineWidth(content, _strokeWidth);
    if(self.realImage){
        [self.realImage drawInRect:self.bounds];
    }

    [_sendPoints enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        CGPoint point = [(NSValue *)obj CGPointValue];
        if(idx == 0){
            CGContextMoveToPoint(content, point.x, point.y);
        }else{
            CGContextAddLineToPoint(content, point.x, point.y);
        }
    }];
    CGContextStrokePath(content);
    UIImage *resImage = UIGraphicsGetImageFromCurrentImageContext();
    self.realImage = resImage;
    UIGraphicsEndImageContext();
    self.image = resImage;
    self.sendPoints = [NSMutableArray array];
    NSLog(@"end: %f",[[NSDate date] timeIntervalSince1970]);
}

@end

2. 画板使用Swift语言

import UIKit
import CoreGraphics

protocol PaintBrush {
    func drawInContext(_ context: CGContext)
}

class BaseBrush: NSObject, PaintBrush {
    var startPoint: CGPoint!
    var endPoint: CGPoint!
    var lastPoint: CGPoint?
    var strokeWidth: CGFloat!

    func drawInContext(_ content: CGContext) {
        assert(false,"need to implement the method in subclass")
    }
}

class PencilBrush: BaseBrush {

    override func drawInContext(_ context: CGContext) {
        if let lastPoint = self.lastPoint{
            context.move(to: lastPoint)
            context.addLine(to: endPoint);
        }else{
            context.move(to: startPoint);
            context.addLine(to: endPoint);
        }
    }
}

class EraserBrush: PencilBrush{

    override func drawInContext(_ context: CGContext) {
        context.setBlendMode(.clear)
        super.drawInContext(context)
    }
}

enum DrawingState {
    case no, began, moved, ended
}

class Board: UIImageView {
    private var drawState: DrawingState!
    private var realImage: UIImage?
    var brush: BaseBrush?
    var strokeWidth: CGFloat
    var strokeColor: UIColor
    var sendPoints: [CGPoint] = []
    override init(frame: CGRect) {
        self.drawState = .no
        self.strokeWidth = 1
        self.strokeColor = UIColor.white
        super.init(frame: frame)
    }

    required init(coder aDecoder: NSCoder) {
        self.drawState = .no
        self.strokeWidth = 1
        self.strokeColor = UIColor.white
        super.init(coder: aDecoder)!
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        if self.brush != nil{
            self.brush?.lastPoint = nil
            self.brush?.startPoint = touches.first?.location(in: self)
            self.brush?.endPoint = self.brush?.startPoint
            self.sendPoints.append((self.brush?.startPoint)!);
            self.drawState = .began
//            self.drawImage()
        }
    }

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        if self.brush != nil{
            self.brush?.endPoint = touches.first?.location(in: self)
            self.sendPoints.append((self.brush?.endPoint)!);
            self.drawState = .moved
//            self.drawImage()
        }
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        if self.brush != nil{
            self.brush?.endPoint = touches.first?.location(in: self)
            self.sendPoints.append((self.brush?.endPoint)!);
            self.drawState = .ended
//            self.drawImage()
            // 发送消息
            self.drawOtherImage()
        }
    }

    override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
        if self.brush != nil{
            self.drawState = .no
            self.brush?.endPoint = nil
            // 发送消息
            self.drawOtherImage()
        }
    }

    // 画自己的线
    func drawImage(){
        if(self.brush == nil){ return; }
        UIGraphicsBeginImageContext(self.bounds.size)

        let content = UIGraphicsGetCurrentContext()
        UIColor.clear.setFill()
        UIRectFill(self.bounds)
        content?.setLineCap(.round)
        content?.setLineWidth(self.strokeWidth)
        content?.setStrokeColor(self.strokeColor.cgColor)

        if let realImage = self.realImage{
            realImage.draw(in: self.bounds)
        }

        brush?.strokeWidth = self.strokeWidth
        brush?.drawInContext(content!)
        content?.strokePath()

        let  previewImage = UIGraphicsGetImageFromCurrentImageContext()
        self.realImage = previewImage
        UIGraphicsEndImageContext()

        self.image = previewImage
        brush?.lastPoint = brush?.endPoint
    }

    // 画发过来的线
    func drawOtherImage() {
        if(self.brush == nil){ return; }
        UIGraphicsBeginImageContext(self.bounds.size)

        let content = UIGraphicsGetCurrentContext()
        UIColor.clear.setFill()
        UIRectFill(self.bounds)
        content?.setLineCap(.round)
        content?.setLineWidth(self.strokeWidth)
        content?.setStrokeColor(self.strokeColor.cgColor)

        if let realImage = self.realImage{
            realImage.draw(in: self.bounds)
        }

        for (n,point) in self.sendPoints.enumerated(){
            if(n == 0){
                content?.move(to: point)
            }else{
                content?.addLine(to: point)
            }
        }
        content?.strokePath()

        let  previewImage = UIGraphicsGetImageFromCurrentImageContext()
        self.realImage = previewImage
        UIGraphicsEndImageContext()

        self.image = previewImage
        self.sendPoints = []
    }
}

推荐阅读更多精彩内容