貝塞爾曲線,聽着挺牛氣一詞,不過下面我們在做畫圖闆的時候就用到貝塞爾繪直線,沒用到繪制曲線的功能。如果會點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):
作者:青玉伏案
出處:http://www.cnblogs.com/ludashi/
本文版權歸作者和共部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。
如果文中有什麼錯誤,歡迎指出。以免更多的人被誤導。
收履歷:某網際網路公司,招聘iOS/Android靠譜工程師,入職後,可内部聯系樓主,有小禮品贈送,有意者可郵箱投遞履歷:[email protected]