• 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];

            }

            

        }

        }

  • 相关阅读:
    团队开发项目客户端——游戏子系统的设计(上)
    团队开发项目客户端——注册子系统的设计
    团队项目开发客户端——登录子系统的设计
    协程库
    Linux下用命令查看CPU ID以及厂家等信息
    c++中字符串的截取:
    C++ STL 的各结构实现
    关于mysql查询最近一条记录
    BerkeleyDB原理及其对应API
    高性能服务器设计(Jeff Darcy's notes on high-performance server design
  • 原文地址:https://www.cnblogs.com/cqb-learner/p/5830026.html
Copyright © 2020-2023  润新知