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, ); //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 = ;
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 = ;
} //旋转的时候调用
- (void)rotation:(UIRotationGestureRecognizer *)rotation
{
// 旋转图片
rotation.view.transform = CGAffineTransformRotate(rotation.view.transform, rotation.rotation); // 复位,只要想相对于上一次旋转就复位
rotation.rotation = ; } //长按的时候调用
// 什么时候调用:长按的时候调用,而且只要手指不离开,拖动的时候会一直调用,手指抬起的时候也会调用
- (void)longPress:(UILongPressGestureRecognizer *)longPress
{ if (longPress.state == UIGestureRecognizerStateBegan) { [UIView animateWithDuration:0.25 animations:^{
//设置为透明
self.imageV.alpha = ;
}completion:^(BOOL finished) {
[UIView animateWithDuration:0.25 animations:^{
self.imageV.alpha = ; //把当前的View做一个截屏
UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, );
//获取上下文
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];
}
}
}
iOS开发Quartz2D 十三:画板涂鸦的更多相关文章
- iOS开发 - Quartz2D画图
Quartz 2D简单介绍 是一个二维画图引擎,同一时候支持iOS和Mac系统 Quartz 2D能完毕的工作 绘制图形 : 线条\三角形\矩形\圆\弧等 绘制文字 绘制\生成图片(图像) 读取\生成 ...
- iOS开发-Quartz2D初识
Quartz2D如果单独的从Quartz,那么会发现Quartz是一个开源的Java作业调度框架,单独从英文翻译的角度来看的话Quartz的英文是石英,如果有的时候不小心搜索会发现手表推荐.本文中介绍 ...
- 从零开始学ios开发(十三):Table Views(下)Grouped and Indexed Sections
在前面2篇关于Table View的介绍中,我们使用的Style都是Plain,没有分组,没有index,这次学习的Table View和iphone中的通讯录很像,有一个个以字符为分割的组,最右边有 ...
- iOS开发之自定义画板
今天整好有时间, 写了一个自定义的画板! [我的github] GLPaint主要采用QuartzCore框架, 对画布上的元素进行渲染, 然后通过UIImageWriteToSavedPhotos ...
- iOS开发Quartz2D之十二:手势解锁实例
一:效果如图: 二:代码: #import "ClockView.h" @interface ClockView() /** 存放的都是当前选中的按钮 */ @property ( ...
- iOS开发Quartz2D十二:手势解锁实例
一:效果如图: 二:代码: #import "ClockView.h" @interface ClockView() /** 存放的都是当前选中的按钮 */ @property ( ...
- iOS开发Quartz2D之八:图形上下文状态栈
#import "DrawView.h" @implementation DrawView - (void)drawRect:(CGRect)rect { // Drawing c ...
- iOS开发Quartz2D之 七:雪花效果
#import "VCView.h" @implementation VCView -(void)awakeFromNib { //[NSTimer scheduledTimerW ...
- iOS开发Quartz2D 三 进度条的应用
一:效果如图: 二:代码 #import "ViewController.h" #import "ProgressView.h" @interface View ...
随机推荐
- bug 7715339 登录失败触发 ‘row cache lock’ 等待
Bug 7715339 - Logon failures causes "row cache lock" waits - Allow disable of logon delay ...
- 终结者:借助pinyin4j相关jar包提取汉字的首字母
import net.sourceforge.pinyin4j.PinyinHelper; import net.sourceforge.pinyin4j.format.HanyuPinyinCase ...
- Kali Linux下安装VMware Tools
引言 Kali Linux是基于Debian的Linux发行版, 设计用于数字取证和渗透測试.安装Kali Linux非常easy,可是安装VMware Tools的过程就有点麻烦了,由于在安装中途会 ...
- Android Support 包里到底有什么
大家假设喜欢我的博客,请关注一下我的微博,请点击这里(http://weibo.com/kifile),谢谢 转载请标明出处(http://blog.csdn.net/kifile),再次感谢 随着 ...
- 热点共享SS网络
# 测试系统: Ubuntu 16.04 LTS-lxde-ARM # ***-libev 安装脚本源于 秋水逸冰: https://teddysun.com/358.html # ss-tproxy ...
- index action分析
上一篇从结构上分析了action的,本篇将以index action为例仔分析一下action的实现方式. 再概括一下action的作用:对于每种功能(如index)action都会包括两个基本的类* ...
- 31.Node.js 常用工具 util
转自:http://www.runoob.com/nodejs/nodejs-module-system.html util 是一个Node.js 核心模块,提供常用函数的集合,用于弥补核心JavaS ...
- 洛谷P2660 zzc 种田
题目背景 可能以后 zzc就去种田了. 题目描述 田地是一个巨大的矩形,然而zzc 每次只能种一个正方形,而每种一个正方形时zzc所花的体力值是正方形的周长,种过的田不可以再种,zzc很懒还要节约体力 ...
- SUSE Linux Enterprise Server 11 64T 安装(带清晰视频)
SUSE Linux Enterprise Server 11 64T 安装实录 650) this.width=650;" onclick='window.open("http: ...
- FineUI 页面跳转
要加 EnableAjax=false; <f:Button ID="btn1" EnableAjax="false" OnClick="btn ...