一:效果如图:
二:代码
#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];
}
}
}