貝塞爾曲線,聽着挺牛氣一詞,不過下面我們在做畫圖板的時候就用到貝塞爾繪直線,沒用到繪制曲線的功能。如果會點PS的小伙伴會對貝塞爾曲線有更直觀的理解。這篇博文的重點不在於如何用使用貝塞爾曲線,而是利用貝塞爾划線的功能來封裝一個畫圖板。
畫圖板的截圖如下,上面的白板就是我們的畫圖板,是自己封裝好的一個UIView,下面會詳細的介紹如何封裝這個畫圖板,下面的控件用來控制我們畫圖板的屬性以及Undo,Redo和保存功能。點擊保存時會把繪制的圖片保存到手機的相冊中。下面是具體的實現方案。
一.封裝畫圖板
其實上面的白板就是一繼承於UiView的一個子類,我們可以在這個子類中添加我們畫圖板相應的屬性和方法,然后實例化成對象添加到ViewController中,當然為了省事添加白板的時候是通過storyboard來完成的,讀者也可以自己實例化然后手動的添加到相應的ViewController中。
1.封裝白板的第一步是新建一個UIView的子類MyView,然后添加相應的屬性和方法。MyView.h中的代碼如下,代碼具體意思請參照注釋
1 #import <UIKit/UIKit.h> 2 3 @interface MyView : UIView 4 //用來設置線條的顏色 5 @property (nonatomic, strong) UIColor *color; 6 //用來設置線條的寬度 7 @property (nonatomic, assign) CGFloat lineWidth; 8 //用來記錄已有線條 9 @property (nonatomic, strong) NSMutableArray *allLine; 10 11 //初始化相關參數 12 -(void)initMyView; 13 //unDo操作 14 -(void)backImage; 15 //reDo操作 16 -(void)forwardImage; 17 18 @end
2、上面的代碼是對外的接口,有些屬性我們是寫在MyView.m的延展中以實現私有的目的,MyView延展部分如下:
1 @interface MyView() 2 //聲明貝塞爾曲線 3 @property(nonatomic, strong) UIBezierPath *bezier; 4 //存儲Undo出來的線條 5 @property(nonatomic, strong) NSMutableArray *cancleArray; 6 @end
3.下面的代碼就是實現部分的代碼了,會根據不同功能給出相應的說明
(1).初始化我們的白板,給線條指定默認顏色和寬度並且給相應的變量分配內存空間,初始化代碼如下:
1 //進行一些初始化工作 2 -(void)initMyView 3 { 4 self.color = [UIColor redColor]; 5 self.lineWidth = 1; 6 self.allLine = [NSMutableArray arrayWithCapacity:50]; 7 self.cancleArray = [NSMutableArray arrayWithCapacity:50]; 8 }
(2)Undo功能的封裝,相當於兩個棧,把顯示的線條出棧,進入為不顯示的線條棧中,每執行一次此操作顯示線條棧中的元素會少一條而不顯示線條棧中會多一條,大致就這個意思吧,代碼如下:
1 //UnDo操作 2 -(void)backImage 3 { 4 if (self.allLine.count > 0) 5 { 6 int index = self.allLine.count - 1; 7 8 [self.cancleArray addObject:self.allLine[index]]; 9 10 [self.allLine removeObjectAtIndex:index]; 11 12 [self setNeedsDisplay 13 ]; 14 } 15 }
(3)Redo操作和Undo操作相反,從未顯示棧中取出元素放入顯示的棧中,代碼中的棧我們是用數組來表示的,代碼如下:
//ReDo操作 -(void)forwardImage { if (self.cancleArray.count > 0) { int index = self.cancleArray.count - 1; [self.allLine addObject:self.cancleArray[index]]; [self.cancleArray removeObjectAtIndex:index]; [self setNeedsDisplay]; } }
(4)、當開始觸摸時我們新建一個BezierPath,把觸摸起點設置成BezierPath的起點,並把將要畫出的線條以及線條對應的屬性封裝成字典添加到顯示棧中,代碼如下
1 -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 2 { 3 //新建貝塞斯曲線 4 self.bezier = [UIBezierPath bezierPath]; 5 6 //獲取觸摸的點 7 UITouch *myTouche = [touches anyObject]; 8 CGPoint point = [myTouche locationInView:self]; 9 10 //把剛觸摸的點設置為bezier的起點 11 [self.bezier moveToPoint:point]; 12 13 //把每條線存入字典中 14 NSMutableDictionary *tempDic = [[NSMutableDictionary alloc] initWithCapacity:3]; 15 [tempDic setObject:self.color forKey:@"color"]; 16 [tempDic setObject:[NSNumber numberWithFloat:self.lineWidth] forKey:@"lineWidth"]; 17 [tempDic setObject:self.bezier forKey:@"line"]; 18 19 //把線加入數組中 20 [self.allLine addObject:tempDic]; 21 22 }
(5)當移動也就是划線的時候把點存儲到BezierPath中,代碼如下
1 -(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event 2 { 3 UITouch *myTouche = [touches anyObject]; 4 CGPoint point = [myTouche locationInView:self]; 5 6 [self.bezier addLineToPoint:point]; 7 8 //重繪界面 9 [self setNeedsDisplay]; 10 11 }
(6)畫出線條
1 // Only override drawRect: if you perform custom drawing. 2 // An empty implementation adversely affects performance during animation. 3 - (void)drawRect:(CGRect)rect 4 { 5 //對之前的線的一個重繪過程 6 for (int i = 0; i < self.allLine.count; i ++) 7 { 8 NSDictionary *tempDic = self.allLine[i]; 9 UIColor *color = tempDic[@"color"]; 10 CGFloat width = [tempDic[@"lineWidth"] floatValue]; 11 UIBezierPath *path = tempDic[@"line"]; 12 13 [color setStroke]; 14 [path setLineWidth:width]; 15 [path stroke]; 16 } 17 18 }
二.畫圖板的使用
上面是封裝畫圖板要用到的全部代碼,下面的代碼就是如何在ViewController中使用我們的畫圖板了,如何實例化控件,以及控件的初始化,注冊回調等在這就不做贅述了,下面給出了主要控件的回調方法
1、通過Slider來調節線條的寬度
1 //通過slider來設置線條的寬度 2 - (IBAction)sliderChange:(id)sender 3 { 4 self.myView.lineWidth = self.mySlider.value; 5 }
2、通過SegmentControl來設置線條的顏色
1 /通過segmentControl來設置線條的顏色 2 - (IBAction)tapSegment:(id)sender { 3 4 switch (self.mySegment.selectedSegmentIndex) { 5 case 0: 6 self.myView.color = [UIColor redColor]; 7 break; 8 case 1: 9 self.myView.color = [UIColor blackColor]; 10 break; 11 case 2: 12 self.myView.color = [UIColor greenColor]; 13 break; 14 15 default: 16 break; 17 } 18 19 }
3、undo和redo操作
1 //Undo 2 - (IBAction)tapBack:(id)sender { 3 [self.myView backImage]; 4 } 5 6 7 //Redo操作 8 - (IBAction)tapGo:(id)sender { 9 [self.myView forwardImage]; 10 }
4.保存操作,也許下面的保存操作在處理方式上略顯笨拙,如有更好的解決方案請留言。 保存的時候我是先截了個屏,然后把白板進行切割,把切割后圖片存入到相冊中,代碼如下:
1 //把畫的圖保存到相冊 2 - (IBAction)tapSave:(id)sender { 3 //截屏 4 UIGraphicsBeginImageContext(self.view.bounds.size); 5 [self.view.layer renderInContext:UIGraphicsGetCurrentContext()]; 6 UIImage *uiImage = UIGraphicsGetImageFromCurrentImageContext(); 7 UIGraphicsEndImageContext(); 8 9 10 //截取畫圖版部分 11 CGImageRef sourceImageRef = [uiImage CGImage]; 12 CGImageRef newImageRef = CGImageCreateWithImageInRect(sourceImageRef, CGRectMake(36, 6, 249, 352)); 13 UIImage *newImage = [UIImage imageWithCGImage:newImageRef]; 14 15 //把截的屏保存到相冊 16 UIImageWriteToSavedPhotosAlbum(newImage , nil, nil, nil); 17 18 //給個保存成功的反饋 19 UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"存儲照片成功" 20 message:@"您已將照片存儲於圖片庫中,打開照片程序即可查看。" 21 delegate:self 22 cancelButtonTitle:@"OK" 23 otherButtonTitles:nil]; 24 [alert show]; 25 26 }
以上就是本畫圖板的主要代碼了,有不足之處還望批評指正。轉載請注明出處。在本文結束時在來幾張截圖吧(demo下載地址:http://www.pgyer.com/LTQ8):