这次介绍一个比较复杂一些的小demo,一个简洁的画板,主要知识点是利用贝塞尔曲线–UIBezierPath这个类去实现手动画线的操作,这个操作在很多app中电子签名这个功能中都曾用到。 先看看示例图,这个小画板有哪些工能
1、基本的画线,可以写字,你可以滑动滑块控制画线的粗细,点击最下方颜色按钮改变画线的颜色
2、点击“撤销”,可以清除你上一步的操作,不断点击,可以不断撤销。如果点击“清屏”,就会清除所有 3、点击“橡皮擦”,可以抹掉屏幕上已经画出的线,滑动滑块可以改变橡皮擦的宽度 4、点击“照片”,可以进入系统相册选择图片 5、可以对图片进行拖动、旋转、放大缩小,然后长按图片,图片会定住,这时候可以在画板上画线 6、点击“保存”,会将你绘制的内容以图片的形式保存到系统相册中
一 、UI控件的布局,如下图,我在storyboard中直接拖入的,绿色区域包含一个toolBar、5个item和1个弹簧,红色区域是1个view,蓝色区域是一个view里包含3个button和1个slider。我直接将这个item和button的action方法拖入到ViewController中
二、 我的项目中新增了两个view,“imageHandleView”和“drawView”; 另外自定义了一个UIBezierPath类,“DrawPath”
三、 首先要写的是DrawView,在这个类里把相应的属性和方法写好,等ViewController调用
DrawView.h
#
import <UIKit/UIKit.h>
@interface DrawView : UIView
/**线宽*/
@property(nonatomic,assign)NSInteger lineWidth;
/**颜色*/
@property(nonatomic,strong)UIColor * pathColor;
/**照片*/
@property(nonatomic,strong)UIImage * imageDraw;
-(
void)clear;
-(
void)undo;
@end
DrawView.m
#import "DrawView.h"
#import "DrawPath.h"
@interface DrawView()
@property(
nonatomic,
strong)DrawPath * path;
@property(
nonatomic,
strong)
NSMutableArray * pathArr;
@end
@implementation DrawView
-(
NSMutableArray *)pathArr
{
if (_pathArr ==
nil) {
_pathArr = [
NSMutableArray array];
}
return _pathArr;
}
-(
void)awakeFromNib
{
[
super awakeFromNib];
[
self setUp];
}
-(instancetype)initWithFrame:(
CGRect)frame
{
if (
self = [
super initWithFrame:frame]) {
[
self setUp];
}
return self;
}
-(
void)setUp
{
UIPanGestureRecognizer * pan = [[UIPanGestureRecognizer alloc]initWithTarget:
self action:
@selector(pan:)];
[
self addGestureRecognizer:pan];
_lineWidth =
1;
_pathColor = [
UIColor blackColor];
}
-(
void)pan:(UIPanGestureRecognizer*)pan
{
CGPoint curP = [pan locationInView:
self];
if (pan
.state == UIGestureRecognizerStateBegan) {
_path = [[DrawPath alloc]init];
_path
.lineWidth = _lineWidth;
_path
.pathSystemColor = _pathColor;
[_path moveToPoint:curP];
[
self.pathArr addObject:_path];
}
[_path addLineToPoint:curP];
[
self setNeedsDisplay];
}
- (
void)drawRect:(
CGRect)rect {
for (DrawPath * path in
self.pathArr) {
if ([path isKindOfClass:[
UIImage class]]) {
UIImage * image = (
UIImage *)path;
[image drawInRect:rect];
}
else{
[path
.pathSystemColor set];
[path stroke];
}
}
}
#pragma mark - 功能
-(
void)clear;
{
[
self.pathArr removeAllObjects];
[
self setNeedsDisplay];
}
-(
void)undo
{
[
self.pathArr removeLastObject];
[
self setNeedsDisplay];
}
-(
void)setImageDraw:(
UIImage *)imageDraw
{
_imageDraw = imageDraw;
[
self.pathArr addObject:_imageDraw];
[
self setNeedsDisplay];
}
@end
四、自定义UIBezierPath,为了给它添加颜色的属性
DrawPath.h
#import <UIKit/UIKit.h>
@interface DrawPath : UIBezierPath
@property(nonatomic,strong)UIColor * pathSystemColor;
@end
五、然后写ImageHandleView,这个view是对image进行操作用的,里面主要写入拖动、旋转、缩放、长按这个几个手势,在用户操作完成后它就会被移除
ImageHandleView.h
#import <UIKit/UIKit.h>
@interface ImageHandleView : UIView
@property(nonatomic,strong)UIImage * imageH;
@property(nonatomic,strong)
void(^handleCompletionBlock)(UIImage * image);
@end
ImageHandleView.m
#import "ImageHandleView.h"
@interface ImageHandleView()<UIGestureRecognizerDelegate>
@property(nonatomic,weak)UIImageView * imageV;
@end
@implementation ImageHandleView
-(
void)setImageH:(UIImage *)imageH
{
_imageH = imageH;
self.imageV.image = imageH;
}
-(UIImageView *)imageV
{
if (_imageV == nil) {
UIImageView * imageV = [[UIImageView alloc]initWithFrame:self.bounds];
imageV.userInteractionEnabled = YES;
_imageV = imageV;
[self setUpGestureRecognizer];
[self addSubview:imageV];
}
return _imageV;
}
#warning 开启这个方法就可以阻止DrawView的pan方法,这样在拖动图片时就不会画出线
-(
void)panHandle
{
NSLog(@
"%s",__func__);
}
#pragma mark - 添加手势
-(
void)setUpGestureRecognizer
{
UIPanGestureRecognizer * panHandle = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(panHandle)];
[self addGestureRecognizer:panHandle];
UIPanGestureRecognizer * pan = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(pan:)];
[_imageV addGestureRecognizer:pan];
UIRotationGestureRecognizer * rotation = [[UIRotationGestureRecognizer alloc]initWithTarget:self action:@selector(rotation:)];
[_imageV addGestureRecognizer:rotation];
UIPinchGestureRecognizer * pinch = [[UIPinchGestureRecognizer alloc]initWithTarget:self action:@selector(pinch:)];
[_imageV addGestureRecognizer:pinch];
UILongPressGestureRecognizer * longPress = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(longPress:)];
[_imageV addGestureRecognizer:longPress];
}
-(
void)pan:(UIPanGestureRecognizer*)pan
{
CGPoint transP = [pan translationInView:self.imageV];
self.imageV.transform = CGAffineTransformTranslate(self.imageV.transform, transP.x, transP.y);
[pan setTranslation:CGPointZero inView:self.imageV];
}
-(
void)rotation:(UIRotationGestureRecognizer*)rotation
{
self.imageV.transform = CGAffineTransformRotate(self.imageV.transform, rotation.rotation);
rotation.delegate = self;
rotation.rotation =
0;
}
-(
void)pinch:(UIPinchGestureRecognizer*)pinch
{
self.imageV.transform = CGAffineTransformScale(self.imageV.transform, pinch.scale, pinch.scale);
pinch.delegate = self;
pinch.scale =
1;
}
-(
void)longPress:(UILongPressGestureRecognizer*)longPress
{
if (longPress.state == UIGestureRecognizerStateBegan) {
[UIView animateWithDuration:
0.25 animations:^{
self.imageV.alpha =
0;
}completion:^(BOOL finished) {
[UIView animateWithDuration:
0.25 animations:^{
self.imageV.alpha =
1;
} completion:^(BOOL finished) {
UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO,
0);
CGContextRef ctx = UIGraphicsGetCurrentContext();
[self.layer renderInContext:ctx];
UIImage * image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
if (_handleCompletionBlock) {
_handleCompletionBlock(image);
}
[self removeFromSuperview];
}];
}];
}
}
#pragma mark - UIGestureRecognizerDelegate
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
@end
六、 最后用ViewController控制以上两个view
ViewCnontroller.m
#import "ViewController.h"
#import "DrawView.h"
#import "ImageHandleView.h"
@interface ViewController ()<UINavigationControllerDelegate,UIImagePickerControllerDelegate>
@property (
weak,
nonatomic)
IBOutlet DrawView *drawView;
@end
@implementation ViewController
- (
void)viewDidLoad {
[
super viewDidLoad];
}
#pragma mark - 清屏
- (
IBAction)clear:(
id)sender {
[_drawView clear];
}
#pragma mark - 撤销
- (
IBAction)undo:(
id)sender {
[_drawView undo];
}
#pragma mark - 橡皮擦
- (
IBAction)eraser:(
id)sender {
_drawView
.pathColor = _drawView
.backgroundColor;
}
#pragma mark - 选择照片
- (
IBAction)pickPhoto:(
id)sender {
UIImagePickerController * pickerVc = [[UIImagePickerController alloc]init];
pickerVc
.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
pickerVc
.delegate =
self;
[
self presentViewController:pickerVc animated:
YES completion:
nil];
}
#pragma mark - UIImagePickerControllerDelegate
-(
void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(
NSDictionary<
NSString *,
id> *)info
{
UIImage * image = info[UIImagePickerControllerOriginalImage];
ImageHandleView * imageHandleV = [[ImageHandleView alloc]initWithFrame:
self.drawView.bounds];
imageHandleV
.handleCompletionBlock = ^(
UIImage *image) {
_drawView
.imageDraw = image;
};
[
self.drawView addSubview:imageHandleV];
imageHandleV
.imageH = image;
[
self dismissViewControllerAnimated:
YES completion:
nil];
}
#pragma mark - 保存
- (
IBAction)save:(
id)sender {
UIGraphicsBeginImageContextWithOptions(_drawView
.bounds.size,
NO,
0);
CGContextRef ctx = UIGraphicsGetCurrentContext();
[_drawView
.layer renderInContext:ctx];
UIImage * image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageWriteToSavedPhotosAlbum(image,
self,
@selector(image:didFinishSavingWithError:contextInfo:),
nil);
}
- (
void)image:(
UIImage *)image didFinishSavingWithError:(
NSError *)error contextInfo:(
void *)contextInfo
{
NSLog(@
"保存图片成功");
UIAlertController * alert = [UIAlertController alertControllerWithTitle:@
"保存成功!" message:
nil preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction * action = [UIAlertAction actionWithTitle:@
"确定" style:UIAlertActionStyleCancel handler:
nil];
[alert addAction:action];
[
self presentViewController:alert animated:
YES completion:
nil];
}
#pragma mark - 线宽控制
- (
IBAction)valueChange:(UISlider *)sender {
_drawView
.lineWidth = sender
.value;
}
#pragma mark - 颜色控制
- (
IBAction)colorChange:(
UIButton *)sender {
_drawView
.pathColor = sender
.backgroundColor;
}
@end
github demo : DrawingBoard