OC写的一个画画板的demo

这是一个画画板的项目部分代码


解锁之后新增了一个UINavgationCOntroller

  • 使用方法
FunctionViewController *vc = [[FunctionViewController alloc] init];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc];
[nav setNavigationBarHidden:YES];
[self presentViewController:nav animated:YES completion:nil];

画图板在手指操作离开屏幕后还能继续画图

  • 定义线模型
// 定义一个数组,lineModel是另一个类,用来装一条线的CGMutablePathRef
@property(nonatomic, strong) NSMutableArray *lineModels; 

  • touchesEnded的时候
  - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
      LineModel *lineModel = [[LineModel alloc] init];
      lineModel.path = CGPathCreateMutableCopy(self.path); // 即使self.path被释放掉也不影响值
      lineModel.lineWidth = 2;
      lineModel.color = [UIColor blackColor];
      [self.lineModels addObject:lineModel];
      
      CGPathRelease(self.path);
      self.path = nil;
      
  }
  • drawRect的时候需要重新绘画
- (void)drawRect:(CGRect)rect {
    // 对存起来的图重新绘制
    [self.lineModels enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        LineModel *model = obj;
        CGContextRef context = UIGraphicsGetCurrentContext();
        CGContextAddPath(context, model.path);
        CGContextSetLineWidth(context, model.lineWidth);
        CGContextSetStrokeColorWithColor(context, model.color.CGColor);
        CGContextDrawPath(context, kCGPathStroke);
    }];
    
    if (self.path) {
        CGContextRef context = UIGraphicsGetCurrentContext();
        CGContextAddPath(context, self.path);
        CGContextSetLineWidth(context, 2);
        CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
        CGContextDrawPath(context, kCGPathStroke);
    }
}

新建了ToolView类,用于进行不同操作的选择

  • 新建6个UIButton、1个UILabel和1个UISlider并且对其初始化
  • 新建每一个组件的吧block方便外部调用
#import <UIKit/UIKit.h>

@interface ToolView : UIView

@property (nonatomic, copy) void(^penBlock)(void);
@property (nonatomic, copy) void(^eraserBlock)(void);
@property (nonatomic, copy) void(^colorBlock)(void);
@property (nonatomic, copy) void(^undoBlock)(void);
@property (nonatomic, copy) void(^clearBlock)(void);
@property (nonatomic, copy) void(^saveBlock)(void);
@property (nonatomic, copy) void(^sliderBlock)(CGFloat width);

@end
  • 在CanvasViewController里面新建一个configToolView的方法来拿到回调需要做的事情
-(void) configToolView {
    
    __weak typeof (self) weakself = self;
    self.toolView.penBlock = ^{
        weakself.bEraserMode = NO;
        weakself.canvasView.color = [UIColor blackColor]; //weakself.lastColor;
        weakself.canvasView.lineWidth = weakself.lastLineWidth;
        
    };
    
    self.toolView.eraserBlock = ^{
        weakself.bEraserMode = YES;
        weakself.canvasView.color = [UIColor whiteColor];
        weakself.canvasView.lineWidth = 50;
    };
    
    self.toolView.colorBlock = ^{
        
    };
    
    self.toolView.undoBlock = ^{
        [weakself.canvasView undo];
    };
    
    self.toolView.clearBlock = ^{
        [weakself.canvasView clear];
    };
    
    self.toolView.saveBlock = ^{
        
    };
    
    self.toolView.sliderBlock = ^(CGFloat width) {
        if (!weakself.bEraserMode) {
            weakself.canvasView.lineWidth = width;
        }
        weakself.lastLineWidth = width;
        
    };
}
  • 新建了变量,用于存储使用橡皮擦之前的线条是什么
    • 3个变量
@property BOOL bEraserMode;
@property UIColor *lastColor;
@property CGFloat lastLineWidth;
  • 在CanvansView.h里面新增2个外部调用的函数
#import <UIKit/UIKit.h>
@interface CanvasView : UIView
...
-(void)undo;
-(void)clear;
...
@end

画图板

  • 新建画图板类
    • 画图板的宽高和屏幕一样,因为方便随便点击就能继续画画
    -(ColorView *) colorView {
    if (!_colorView) {
        _colorView = [[ColorView alloc] initWithFrame:self.view.frame];
    }
    return _colorView;
    }
  • 画板类下面加了个bottomView,用来选颜色用

  • 在bottomView中加了10个颜色button,用来对不同颜色的选择

  • 新增一个block提供给CanvasViewController来调用,用来传输颜色

@interface ColorView : UIView
...
@property (nonatomic, copy) void(^selectColorBlock)(UIColor *color);
...
@end
  • 在CanvasViewController调用
-(ColorView *) colorView {
    if (!_colorView) {
        _colorView = [[ColorView alloc] initWithFrame:self.view.frame];
        __weak typeof (self) weakify = self;
        _colorView.selectColorBlock = ^(UIColor *color) {
            if (!weakify.bEraserMode) {
                weakify.canvasView.color = color;
            }
            weakify.lastColor = color;
        };
    }
    return _colorView;
}

保存到相册

  • 设置权限
    // 在info.pkist下加入这两项
    <key>NSPhotoLibraryUsageDescription</key>
    <string>使用照片</string>
    <key>NSPhotoLibraryAddUsageDescription</key>
    <string>使用照片</string>
    
  • 在CanvasViewController里面加上代码
// 加入两个新的包
#import <Photos/Photos.h>
#import <AssetsLibrary/AssetsLibrary.h>
...
// 在回调block调用
self.toolView.saveBlock = ^{
    [weakself saveImageToAlbum:[weakself screenView:weakself.canvasView]];
        
};
...
// 截屏
-(UIImage *) screenView:(UIView *)view {
    CGRect rect = view.frame;
    UIGraphicsBeginImageContext(rect.size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    [view.layer renderInContext:context];
    UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return img;
}
...
/**
 保存图片

 @param image 返回image
 */
-(void) saveImageToAlbum:(UIImage *)image {
    
    // 判断有没有相册权限
    PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];
    if (status == PHAuthorizationStatusDenied || status == PHAuthorizationStatusRestricted) {
        NSLog(@"没有访问权限");
    } else {
        UIImageWriteToSavedPhotosAlbum(image, self, @selector(image:didFinishSavingWithError:contextInfo:),(__bridge void *)self);
        
        
    }
}

源码

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,163评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,301评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,089评论 0 352
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,093评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,110评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,079评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,005评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,840评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,278评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,497评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,394评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,980评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,628评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,649评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,548评论 2 352

推荐阅读更多精彩内容

  • 1.ios高性能编程 (1).内层 最小的内层平均值和峰值(2).耗电量 高效的算法和数据结构(3).初始化时...
    欧辰_OSR阅读 29,370评论 8 265
  • OC语言基础 1.类与对象 类方法 OC的类方法只有2种:静态方法和实例方法两种 在OC中,只要方法声明在@int...
    奇异果好补阅读 4,270评论 0 11
  • 身临其境,似有我,若无我,身外之物化作烟雾散去,似乎天地间只剩下一个我,一盏茶;刚刚找到自己又飘然忘却此身。“长恨...
    红红的雨阅读 191评论 0 0
  • 古人类的故事一直是我感兴趣的内容,回顾这段我们还不算“人”的历程,我们今天面对的很多问题就有了一个新的视角。...
    碎叶屋阅读 658评论 2 3
  • 1.时间是无法管理的,唯一能管理的只有自己。 2.德鲁克说,把每天发生的事情记录下来,分析每一件事情,最后才能够安...
    一鱼一菩提阅读 335评论 0 1