iOS开发Quartz2D 十三:画板涂鸦

时间:2023-02-08 15:47:16

 

一:效果如图:

iOS开发Quartz2D 十三:画板涂鸦

 

二:代码

#import "ViewController.h"
#import "DrawView.h"
#import "HandleImageView.h"

@interface ViewController ()<UINavigationControllerDelegate,UIImagePickerControllerDelegate,handleImageViewDelegate>
@property (weak, nonatomic) IBOutlet DrawView *drawView;

@end

@implementation ViewController

/**
 *    1:属于谁的东西,应该在谁里面去写,尽量减少控制器中的代码。因为设置的都在self.drawView上进行绘制的
 *
 */

//清屏
- (IBAction)clear:(id)sender {
    [self.drawView clear];
}

//撤销
- (IBAction)undo:(id)sender {
    [self.drawView undo];
}

//橡皮擦
- (IBAction)erase:(id)sender {
    [self.drawView erase];
}

//设置线的宽度
- (IBAction)setLineWidth:(UISlider *)sender {
    [self.drawView setLineWith:sender.value];
}

//设置线的颜色
/**
 *sender.backgroundColor:调用的btn的get方法获得是背景色,点击不同的按钮,将不同按钮的背景色传递给self.drawView
 */
- (IBAction)setLineColor:(UIButton *)sender {
    [self.drawView setLineColor:sender.backgroundColor];
}

/**
 *    1:UIImagePickerController:能更改中文,但是需要遵守UINavigationControllerDelegate,UIImagePickerControllerDelegate两个协议,可以指定sourceType,delegate,presentViewController弹出,在代理可以根据sourceType类型做判断,必须手动去dissmiss
 *
 */
//照片
- (IBAction)photo:(id)sender {
    //从系统相册当中选择一张图片
    //1.弹出系统相册
    UIImagePickerController *pickerVC = [[UIImagePickerController alloc] init];
    
    //设置照片的来源
    pickerVC.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
    pickerVC.delegate = self;
    //modal出系统相册
    [self presentViewController:pickerVC animated:YES completion:nil];
}

/**
 * 1: 不用ImageView的原因是,ImageView经过手势缩放等处理后,不知道了其实际的尺寸,所以用UIView中放UImageView,上下文的大小就为UIView大小
   2:要想保存图片,先要生成图片,开启上下文,将drawView的layer绘制到上下文中(将UIView绘制到drawView的上下文中),得到图片之后UIImageWriteToSavedPhotosAlbum,写入系统的相册,注意:@selector里面的方法不能够瞎写,必须得是image:didFinishSavingWithError:contextInfo:
 *
 */
//保存
- (IBAction)save:(id)sender {
    //把绘制的东西保存到系统相册当中
    
    //1.开启一个位图上下文
    UIGraphicsBeginImageContextWithOptions(self.drawView.bounds.size, NO, 0);
    
    
    //2.把画板上的内容渲染到上下文当中
    CGContextRef ctx =  UIGraphicsGetCurrentContext();
    [self.drawView.layer renderInContext:ctx];
    
    //3.从上下文当中取出一张图片
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    
    //4.关闭上下文
    UIGraphicsEndImageContext();
    
    //5.把图片保存到系统相册当中
    //注意:@selector里面的方法不能够瞎写,必须得是image:didFinishSavingWithError:contextInfo:
    UIImageWriteToSavedPhotosAlbum(newImage, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);
    
    
}

//保存完毕时调用
- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo {
    NSLog(@"success");
    
}
//- (void)saveSuccess {
//    NSLog(@"success");
//}




- (void)viewDidLoad {
    [super viewDidLoad];
   
}

#pragma mark -- 隐藏状态栏
- (BOOL)prefersStatusBarHidden {
    return YES;
}

/**
 *1:打印info信息:通过key值获取image,将image转化成二进制流的NSData形式,UIImagePNGRepresentation,UIImageJPEGRepresentation,两种转化方式,png保持原图不压缩,内存大,JPEG可以限制压缩系数,越大图片越不清晰,转化成NSData,writeToFile写入桌面
  2:先用一个UIView,添加到self.view上,UIView再添加UIImageView,再将整个View的layer绘制到上下文中,UIView设置成透明色,则UIView下面的路径就会显示出来了
 *
 */
//当选择某一个照片时,会调用这个方法
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info {

    NSLog(@"%@",info);
    UIImage *image  = info[UIImagePickerControllerOriginalImage];

    //NSData *data = UIImageJPEGRepresentation(image, 1);
    NSData *data = UIImagePNGRepresentation(image);
    //[data writeToFile:@"/Users/xiaomage/Desktop/photo.jpg" atomically:YES];
    [data writeToFile:@"/Users/xiaomage/Desktop/photo.png" atomically:YES];
    
    HandleImageView *handleV = [[HandleImageView alloc] init];
    handleV.backgroundColor = [UIColor clearColor];
    handleV.frame = self.drawView.frame;
    handleV.image = image;
    handleV.delegate = self;
    [self.view addSubview:handleV];
    
    
    
    
    //self.drawView.image = image;
    //取消弹出的系统相册 
    [self dismissViewControllerAnimated:YES completion:nil];
    
    
}


-(void)handleImageView:(HandleImageView *)handleImageView newImage:(UIImage *)newImage {
    
    self.drawView.image = newImage;
    
}



- (void)pan:(UIPanGestureRecognizer *)pan {
    
    CGPoint transP = [pan translationInView:pan.view];
    pan.view.transform = CGAffineTransformTranslate(pan.view.transform, transP.x, transP.y);
    
    //复位
    [pan setTranslation:CGPointZero inView:pan.view];
    
}

/**
 *    初始界面的搭建:
 1:状态栏的改变:1:全局状态栏的设定,配合pilist中设置viewcontrollerbase 设置其值为no 2:在控制器中实现方法,prefersStatusBarHidden,和状态栏样式的方法
 2:界面的搭建:在搭建控制器或是view的界面的时候:1:先分析界面的UI结构由哪些控件组成,采用分层封装的思想,将控制器或是view的零散控件封装在一个整体的模块中(xib也类似),如何分层?参考新浪微博cell的分层封装,如何封装view,参考view的封装方法.2:若是项目中都会用到这些模块,就想到抽成父类,让子类去继承,不同的部分分别在子类中实现,若是父类想和子类进行关联,则父类提供方法供子类去重写,则父类也可以拿到该值。
 
 3:1:对于顶部的UI,可以采用view封装,但是要考虑横屏竖屏的适配,很麻烦,所以可以用toolBar控件,里面自动适配toolBar中的按钮,拖一个toolBar,往里面添加toolBaritem,保存按钮的添加,就添加一个弹簧,还可以对弹簧进行设置,就会出现上图的效果。2:底部UI的构建:1:三个按钮横屏竖屏的适配,先设置左侧按钮左间距底部间距,右侧间距,中间按钮右侧间距底部间距,最右侧按钮右侧间距底部间距,三个按钮等宽,则可以实现三个按钮平分屏幕的宽度,在设置按钮的高度,slider设置,左右顶部高度,大view不用设置高度了,其高度会等于所有子控件的高度之和。2:ios9新出来一个控件,StackView,可以解决三个按钮的适配,直接将按钮拖进去,设置间距就可以了,自动适配
 
 2:在拖线设置的时候,按住shift键,再拖线同时设置多个间距
 
 */
@end
#import <UIKit/UIKit.h>

@interface DrawView : UIView

//清屏
- (void)clear;
//撤销
- (void)undo;
//橡皮擦
- (void)erase;
//设置线的宽度
- (void)setLineWith:(CGFloat)lineWidth;
//设置线的颜色
- (void)setLineColor:(UIColor *)color;

/** 要绘制的图片 */
@property (nonatomic, strong) UIImage * image;



@end
#import "DrawView.h"
#import "MyBezierPath.h"

@interface DrawView()

/** 当前绘制的路径 */
@property (nonatomic, strong) UIBezierPath *path;

//保存当前绘制的所有路径
@property (nonatomic, strong) NSMutableArray *allPathArray;

//当前路径的线宽
@property (nonatomic, assign) CGFloat width;

//当前路径的颜色
@property (nonatomic, strong) UIColor *color;

@end

@implementation DrawView


- (NSMutableArray *)allPathArray {
    
    if (_allPathArray == nil) {
        _allPathArray = [NSMutableArray array];
    }
    return _allPathArray;
}

/**
 *    1:一般一次性设置的内容都放在初始化方法init中或是awakefromNib中
    2:添加拖拽pan手势用于绘图
 */
- (void)awakeFromNib {
    //添加手势
    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
    [self addGestureRecognizer:pan];
    
    self.width = 1;
    self.color = [UIColor blackColor];
}


-(void)setImage:(UIImage *)image {
    _image = image;
    
    //添加图片添加到数组当中
    [self.allPathArray addObject:image];
    //重绘
    [self setNeedsDisplay];
}

/**
 *    3:方法的封装,一般我们在控制器写代码的时候,遇到的业务逻辑要想到去封装,尽量减少控制器的代码,封装的代码和谁有关系就封装到哪里:1:能分类封装就分类封装,减少类的创建,节省空间,一个参数则可以直接选类方法,两个参数建议使用对象方法,在分类中属性定义某个变量,不会生成下划线的成员变量,需要自己重写set'方法和get方法,才能获得或是修改变量,若是在普通类中用readonly修饰,则在本类中可以调用get方法,但是不能使用set方法,可以使用下划线的成员变量,但是子类继承父类后,不能继承带下划线的成员变量,不能调用set,只能调用get 2:封装成工具类:单例或是类方法,若是单例,则尽量将方法抽成属性,通过重写封装 ,类方法,想获得单一一样的成员变量则可以static定义全局或是局部变量。3:继承:把相同的部分都封装在父类中,不同的部分让子类去单独实现,若是父类想和子类进行数据沟通,则父类可以提供方法,供子类去重写 4:三种方法封装的主要思想就是:将与外界无关的逻辑全部封装在类的内部,类在提供接口供外界访问,让外界调用是最简洁的
 *
 */




/**
 *清屏:清屏就是要移除所有的路径,此时删除大数组中的所有的路径就可以,在调用setNeedsDisplay,进行重绘,此时数组中没有了任何一条路径,所以就会清空上下文
 */
//清屏
- (void)clear {
    //清空所有的路径
    [self.allPathArray removeAllObjects];
    //重绘
    [self setNeedsDisplay];
    
}

/**
 *撤销:即是取出路径数组中的最后一个路径删除,并调用setNeedsDisplay
 */
//撤销
- (void)undo {
    //删除最后一个路径
    [self.allPathArray removeLastObject];
    //重绘
    [self setNeedsDisplay];
}

/**
 *    橡皮擦功能就是:又绘制了一条路径,只是设置路径的颜色为白色,将其他颜色的路径覆盖掉
 */
//橡皮擦
- (void)erase {
    [self setLineColor:[UIColor whiteColor]];
}



//设置线的宽度
- (void)setLineWith:(CGFloat)lineWidth {
    self.width = lineWidth;
}

//设置线的颜色
/**
 *    设置线的颜色,应该考虑到当没有设置颜色的时候,或传入的参数为空值的时候,所以要考虑以上两点,所以要设置线的默认颜色,一次性设置,在init或是awakefromNib中去设置
 *
 *    @param color
 */
- (void)setLineColor:(UIColor *)color {
    self.color = color;
}


#pragma mark -- drawview的pan拖拽手势,画线
/**
 *2:在拖拽手势方法中:1:绘制UIBezierPath路径:开始设置起点,change的时候添加联系,并调用setNeedsDisplay,异步调用drawRect方法  2:定义一个全局数组,用于保存所有的路径,最后需要遍历数组,将所有路径取出来,绘制到上下文中 3:只有在自定义view中才能重写drawRect方法,且drawRect方法配合setNeedsDisplay使用,此方法必须由系统调用才会生成与view相关联的上下文,其中路径可以在其他方法中绘制,但是最后将路径绘制到上下文中的时候就必须在drawRect方法中实现[phath stroke];或是[path fill];
 
    2:什么情况下自定义类或是控件:1:当发现系统原始的功能,没有办法瞒足自己需求时,这个时候,要自定义类.继承系统原来的东西.再去添加属性自己的东西. 2:在begin方法中每次都创建一个全新的路径,因为在一次绘制的时候begin方法只执行一次,将每一次创建的路径都保存在大数组中,在drawrect中遍历,得到路径去绘制。其中颜色的绘制必须在drawrect上下文中绘制,否则不会显示,因为路径path没有保存color,但是线宽有保存,所以自定义类MyBezierPath继承UIBezierPath,提供color属性,就是为了保存color,在draw遍历时取出path后,直接设置路径颜色。
 */
- (void)pan:(UIPanGestureRecognizer *)pan {
    
    //获取的当前手指的点
    CGPoint curP = [pan locationInView:self];
    //判断手势的状态
    if(pan.state == UIGestureRecognizerStateBegan) {
        //创建路径
        //UIBezierPath *path = [UIBezierPath bezierPath];
        MyBezierPath *path = [[MyBezierPath alloc] init];
        self.path = path;
        //设置起点
        [path moveToPoint:curP];
        
        //设置线的宽度
        [path setLineWidth:self.width];
        //设置线的颜色
        //什么情况下自定义类:当发现系统原始的功能,没有办法瞒足自己需求时,这个时候,要自定义类.继承系统原来的东西.再去添加属性自己的东西.
        path.color = self.color;
   
        [self.allPathArray addObject:path];
        
    } else if(pan.state == UIGestureRecognizerStateChanged) {
        
        //绘制一根线到当前手指所在的点
        [self.path addLineToPoint:curP];
        //重绘
        [self setNeedsDisplay];
    }
    
}
/**
 * 1:当遍历的时候,若是数组中含有的不只是同一种类型的对象,在遍历的时候可以每个对象指定同一个类型的对象,再根据iskindofclass来判断对象具体是那种类型。
   2:当画图片的时候:直接用image调用[image drawInRect:rect];或是drawpoint
 *
 */
-(void)drawRect:(CGRect)rect {
    
    //绘制保存的所有路径
    for (MyBezierPath *path in self.allPathArray) {
        //判断取出的路径真实类型
        if([path isKindOfClass:[UIImage class]]) {
            UIImage *image = (UIImage *)path;
            [image drawInRect:rect];
        }else {
            [path.color set];
            [path stroke];
        }
    }
}

@end
#import <UIKit/UIKit.h>

@interface MyBezierPath : UIBezierPath

/** 当前路径的颜色 */
@property (nonatomic, strong) UIColor *color;

@end
#import "MyBezierPath.h"

@implementation MyBezierPath

@end
#import <UIKit/UIKit.h>

@class HandleImageView;
@protocol handleImageViewDelegate <NSObject>

- (void)handleImageView:(HandleImageView *)handleImageView newImage:(UIImage *)newImage;

@end


@interface HandleImageView : UIView

/** <#注释#> */
@property (nonatomic, strong) UIImage *image;

/** <#注释#> */
@property (nonatomic, weak) id<handleImageViewDelegate> delegate;

@end
#import "HandleImageView.h"

@interface HandleImageView()<UIGestureRecognizerDelegate>

/** 在UIView上添加一张 UIImageView */
@property (nonatomic, weak) UIImageView *imageV;

@end

@implementation HandleImageView

/**
 *1: 懒加载UIImageView,属性修饰也可以用weak修饰,能用weak的时候尽量用weak,其中_imageV = imageV赋值的时候既可以写在添加[self addSubview:imageV];之前也可以写在其之后
 *
 *    @return UIImageView
 */
-(UIImageView *)imageV {
    
    if (_imageV == nil) {
        UIImageView *imageV = [[UIImageView alloc] init];
        imageV.frame = self.bounds;
        imageV.userInteractionEnabled = YES;
        [self addSubview:imageV];
        _imageV = imageV;
        //添加手势
        [self addGes];
    }
    return _imageV;
}

-(void)setImage:(UIImage *)image {
    _image = image;
    
    NSLog(@"%@",self.imageV);
    self.imageV.image = image;
    
}


/**
 * 2:添加手势:1:添加了拖拽pan,长按longpress,捏合手势pinch,旋转手势:rotation.1:这些手势都分三种状态,开始,改变,结束,其中在使用这些手势一直绘制的时候,开始只调用一次 2:在这些手势中都可以获得触摸点,locationInView,还可以拖拽距离translationInView,点击的view,旋转角度,捏合比例,而且若是想相对上次改变,则一定要进行复位操作  3:若是想同时支持多个手势,需要将添加的手势设置手势代理,实现otherGestureRecognizer代理方法返回YES,则这样就可以同时支持多个手势。一般涉及旋转平移缩放都与transform一起用,累加形变和非累加形变。 4:复位操作:复位,只要想相对于上一次旋转就复位 [pan setTranslation:CGPointZero inView:pan.view]; pinch.scale = 1;rotation.rotation = 0;
 *
 */
//添加手势
-(void)addGes{
    
    // pan
    // 拖拽手势
    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc]
                                   initWithTarget:self action:@selector(pan:)];
    
    [self.imageV addGestureRecognizer:pan];
    
    // pinch
    // 捏合
    UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(pinch:)];
    
    pinch.delegate = self;
    [self.imageV addGestureRecognizer:pinch];
    
    
    //添加旋转
    UIRotationGestureRecognizer *rotation = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(rotation:)];
    rotation.delegate = self;
    
    [self.imageV addGestureRecognizer:rotation];
    
    // 长按手势
    UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPress:)];
    [self.imageV addGestureRecognizer:longPress];
    
}




//捏合的时候调用.
- (void)pinch:(UIPinchGestureRecognizer *)pinch
{
    
    pinch.view.transform = CGAffineTransformScale( pinch.view.transform, pinch.scale, pinch.scale);
    // 复位
    pinch.scale = 1;
}


//旋转的时候调用
- (void)rotation:(UIRotationGestureRecognizer *)rotation
{
    // 旋转图片
    rotation.view.transform = CGAffineTransformRotate(rotation.view.transform, rotation.rotation);
    
    // 复位,只要想相对于上一次旋转就复位
    rotation.rotation = 0;
    
}


//长按的时候调用
// 什么时候调用:长按的时候调用,而且只要手指不离开,拖动的时候会一直调用,手指抬起的时候也会调用
- (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;
                
                //把当前的View做一个截屏
                UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0);
                //获取上下文
                CGContextRef ctx = UIGraphicsGetCurrentContext();
                [self.layer renderInContext:ctx];
                UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
                //关闭上下文.
                UIGraphicsEndImageContext();
   
                //调用代理方法
                if([self.delegate respondsToSelector:@selector(handleImageView:newImage:)]) {
                    
                    [self.delegate handleImageView:self newImage:newImage];
                }
                
                //从父控件当中移除
                [self removeFromSuperview];
                
            }];
        }];
        
        
    }
    
}

//拖动的时候调用
- (void)pan:(UIPanGestureRecognizer *)pan{
    
    CGPoint transP = [pan translationInView:pan.view];
    
    pan.view.transform = CGAffineTransformTranslate(pan.view.transform, transP.x, transP.y);
    //复位
    [pan setTranslation:CGPointZero inView:pan.view];
    
    
}


//能够同时支持多个手势
-(BOOL)gestureRecognizer:(nonnull UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(nonnull UIGestureRecognizer *)otherGestureRecognizer{
    
    return YES;
}
@end

 

画板界面分析.

顶部是一个工具栏.有清屏,撤销,橡皮擦,照片功能.最右部是一个保存按钮

中间部分为画板区域

最下部拖动滑竿能够改变画笔的粗线.可以选颜色.

 

1.界面搭建

最上部为一个ToolBar,往ToolBar拖些item,使用ToolBar的好处.里面按钮的位置不需要我们再去管理.

给最上部的工具栏做自动布局.离父控件左,上,右都为0,保存工具条的高度不度

 

拖一个UIView当前下部的View.在下部的View当中拖累三个按钮,设置每一个按钮的背景颜色.

点击每一按钮时办到设置画笔的颜色.

其中三个按钮只间的间距始终保存等,每一个按钮的宽度和高度都相等.通过自动布局的方式办到.

 

先把这个UIView的自动布局设好, 让其左,右,下都是0,高度固定.

 

自动布局设置为:第一个按钮高度固定,与左,右,下都保存20个间距.

第二个按钮与第一个按钮,高度,宽度,centerY都相等.与右边有20个间距.

第三个按钮也是第一个按钮的高度,宽度,centerY都相等.与右边有20个间距,最右边也保存20个间距.

 

最后是中间的画板区域,画板区域只需要上距离上下左右都为0即可.

 

2.实现画板功能.

 

  当手指移动的时候,在中间的画板区域当中进行绘制.由于每一个路径都有不同的状态.所以我们就不能用一条路径来做.

  所以要弄一个数组记录住每一条路径.

  实现画板功能.

  1.监听手指在屏幕上的状态.在开始点击屏幕的时候,创建一个路径.并把手指当前的点设为路径的起点.

 

    弄一个成员属性记录当前绘制的路径.并把当前的路径添加到路径数组当中.

   

  2.当手指在移动的时候,用当前的路径添加一根线到当前手指所在的点.然后进行重绘.

  3.在绘图方法当中.取出所有的路径.把所有的路径给绘制出来.

 

3.设置路径属性.

提供属性方法.

 

清屏功能:删除所有路径进行重绘

 

撤销功能:删除最后一条路径,进行重绘

 

设置线宽:

由于每一条线宽度都不样.所以要在开始创建路径的时,就要添加一个成员属性,设置一个默认值.

在把当前路径添加到路径数组之前,设置好线的宽度.然后重写线宽属性方法.

下一次再创建路径时,线的宽度就是当前设置的宽度.

设置线的颜色:

同样,由于每一条线的颜色也不一样.也需要记录住每一条路径的颜色.

由于UIBezierPath没有给我们直接提供设置颜色的属性.我们可以自定义一个UIBezierPath.

创建一个MyBezierPath类,继承UIBezierPath,在该类中添加一个颜色的属性.

在创建路径的时候,直接使用自己定义的路径.设置路径默认的一个颜色.方法给设置线宽一样.

在绘图过程中, 取出来的都是MyBezierPath,把MyBezierPath的颜色设置路径的颜色.

 

橡皮擦功能:橡皮擦功能其实就是把路径的颜色设为白色.

 

 

4.保存绘制的图片到相册.

保存相册的思路:就是把绘制的在View上的内容生成一张图片,保存到系统相册当中.

具体步骤:

开启一个跟View相同大小的图片上下文.

把View的layer上面内容渲染到上下文当中.

生成一张图片,把图片保存到上下文.

关闭上下文.

如何把一张图片保存到上下文?

调用方法:

参数说明:

第一个参数:要写入到相册的图片.

第二个参数:哪个对象坚听写入完成时的状态.

第三个参数:图片保存完成时调用的方法

UIImageWriteToSavedPhotosAlbum(newImage,

  self,

@selector(image:didFinishSavingWithError: contextInfo:),nil);

注意:图片保存完成时调用的方法必须得是image:didFinishSavingWithError: contextInfo:

 

 

5.选择图片.

点击图片时弹出系统的相册.

如果弹出系统的相册?

使用UIImagePickerController控件器Modal出它来.

UIImagePickerController *pick = [[UIImagePickerController alloc] init];

 

设置照片的来源

  pick.sourceType =  UIImagePickerControllerSourceTypeSavedPhotosAlbum;

 

  设置代码,监听选择图片,UIImagePickerController比较特殊,它需要遵守两个协议

  <UINavigationControllerDelegate,UIImagePickerControllerDelegate>

  pick.delegate = self;

 

  modal出控件器

  [self presentViewController:pick animated:YES completion:nil];

 

  注意没有实现代码方法时,选择一张照片会自动的dismiss掉相册控制器.但是设置代码后,就得要自己去dismiss了

 

 

  实现代理方法.

  选择的照片就在这个方法第二个参数当中, 它是一个字典

  -(void)imagePickerController:(nonnull UIImagePickerController *)picker

  didFinishPickingMediaWithInfo:(nonnull NSDictionary<NSString *,id> *)info{

 

获取当前选中的图片.通过UIImagePickerControllerOriginalImage就能获取.

UIImage *image = info[UIImagePickerControllerOriginalImage];

 

}

 

获取完图片后.图片是能够缩放,平移的,因此获取完图片后,是在画板板View上面添加了一个UIView.

只有UIView才能做平移,缩放,旋转等操作.

因此做法为.在图片选择完毕后,添加一个和画板View相同大小的UIView,这个UIView内部有一个UIImageView.

对这个UIImageView进行一些手势操作.操作完成时.长按图片,把View的内容截屏,生成一张图片.

把新生成的图片绘制到画板上面.

 

6.绘制图片.

在画板View当中提供一个UImage属性,供外界传递.重写属性的set方法,每次传递图片时进行重绘

画图片也有有序的,所以要把图片也添加到路径数组当中.

在绘图片过过程当中,如果发现取出来的是一个图片类型.那就直接图片绘制到上下文当中.

 

具体实现代码为:

-(void)drawRect:(CGRect)rect{

 

    for (DrawPath *path in self.pathArray) {

        

        if ([path isKindOfClass:[UIImage class]]) {

            

         UIImage *image = (UIImage *)path;

            [image drawInRect:rect];

 

        }else{

            [path.lineColor set];

            [path stroke];

        }

        

    }

    }