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

}

}

}

时间: 2024-11-05 15:12:14

iOS开发Quartz2D 十三:画板涂鸦的相关文章

从零开始学ios开发(十三):Table Views(下)Grouped and Indexed Sections

在前面2篇关于Table View的介绍中,我们使用的Style都是Plain,没有分组,没有index,这次学习的Table View和iphone中的通讯录很像,有一个个以字符为分割的组,最右边有一列小字符作为index,最顶端有一个搜索栏可以进行搜索,好了,下面开始这次的学习. 1)创建一个新的项目,template选择Single View Application,命名为Sections 2)添加Table View,连接delegate和data source到File's Owner

iOS开发 - Quartz2D绘图

Quartz 2D简介 是一个二维绘图引擎,同时支持iOS和Mac系统 Quartz 2D能完成的工作 绘制图形 : 线条\三角形\矩形\圆\弧等 绘制文字 绘制\生成图片(图像) 读取\生成PDF 截图\裁剪图片 自定义UI控件 - - drawRect:方法的使用 常见图形的绘制:线条.多边形.圆 绘图状态的设置:文字颜色.线宽等 图形上下文状态的保存与恢复 图形上下文栈 为了便于搭建美观的UI界面,iOS提供了UIKit框架,里面有各种各样的UI控件 UILabel:显示文字 UIImag

iOS开发-Quartz2D初识

Quartz2D如果单独的从Quartz,那么会发现Quartz是一个开源的Java作业调度框架,单独从英文翻译的角度来看的话Quartz的英文是石英,如果有的时候不小心搜索会发现手表推荐.本文中介绍的Quartz是位于MAC OS X的Drawin核心之上的绘图层,有时候也认为是CoreGraphics.Quartz直接地支持Aqua,借由显示2D绘图图形来创建用户接口,包含实时绘制(rendering)和次像素(sub-pixel)精准的反锯齿,由Quartz Compositor和Quar

iOS开发之自定义画板

今天整好有时间, 写了一个自定义的画板!  [我的github] GLPaint主要采用QuartzCore框架, 对画布上的元素进行渲染, 然后通过UIImageWriteToSavedPhotosAlbum保存到相册. 代码耦合性比较低, 集成比较简单, 初学者可以了解一下. 1. 在ViewController中只需要实现这几个方法基本就没问题了; 1 #pragma mark - 创建colorPiker 2 - (void)setupColorPiker 3 { 4 _piker =

iOS开发 - Quartz2D绘制小黄人

Quartz2D绘制小黄人 - (void)drawRect:(CGRect)rect { // 1.上下文 CGContextRef ctx = UIGraphicsGetCurrentContext(); // 2.身体 drawBody(ctx, rect); // 3.嘴(微笑) drawMouth(ctx, rect); // 4.画眼睛 drawEyes(ctx, rect); } /** * 眼睛 */ void drawEyes(CGContextRef ctx, CGRect

iOS开发Quartz2D 三 进度条的应用

一:效果如图: 二:代码 #import "ViewController.h" #import "ProgressView.h" @interface ViewController () @property (weak, nonatomic) IBOutlet UILabel *valueTitle; @property (weak, nonatomic) IBOutlet ProgressView *progressView; @end @implementati

iOS开发-Quartz2D绘制时定时器选择

NSTimer定时器 [NSTimer scheduledTimerWithTimeInterval:0.025 target:self selector:@selector(update) userInfo:nil repeats:YES]; //如果我们使用NSTimer定时器. 设置的执?行时间为0.025秒, 假如屏幕刷新时间为0.035.中间就会等待0.010//绘制图形的时候不建议使用该方法 CADisplayLink定时器 补充知识: drawRect方法是在view将要显示,已经

IOS开发——UI进阶篇(十五)Quartz2D介绍

一.Quartz2D简介 1.什么是Quartz2DQuartz 2D是一个二维绘图引擎,同时支持iOS和Mac系统 Quartz 2D能完成的工作绘制图形 : 线条\三角形\矩形\圆\弧等绘制文字绘制\生成图片(图像)读取\生成PDF截图\裁剪图片自定义UI控件 2.Quartz2D实例Quartz 2D能做很多强大的事情,例如 裁剪图片 涂鸦\画板 手势解锁 报表:折线图\饼状图\柱状图 二.自定义view 1.Quartz2D在iOS开发中的价值为了便于搭建美观的UI界面,iOS提供了UI

iOS开发UI篇—Quartz2D使用(绘图路径)

iOS开发UI篇-Quartz2D使用(绘图路径) 一.绘图路径 A.简单说明 在画线的时候,方法的内部默认创建一个path.它把路径都放到了path里面去. 1.创建路径  cgmutablepathref 调用该方法相当于创建了一个路径,这个路径用来保存绘图信息. 2.把绘图信息添加到路径里边. 以前的方法是点的位置添加到ctx(图形上下文信息)中,ctx 默认会在内部创建一个path用来保存绘图信息. 在图形上下文中有一块存储空间专门用来存储绘图信息,其实这块空间就是CGMutablePa