最近在做一個(gè)簽字版的模塊,需要用到了貝塞爾曲線來(lái)畫蹲堂。其實(shí)狼讨,做這種畫板有很多方法,可以用UIGraphics來(lái)畫柒竞,也可以用OPenGL來(lái)畫政供,只是當(dāng)時(shí)選擇了貝塞爾曲線,沒(méi)想到還入了坑朽基。
看下面兩張圖:
明顯的能夠看到下圖要比上圖圓滑好看一些布隔。
因?yàn)槲覀冊(cè)偈褂秘惾麪柷€的時(shí)候一般是:
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(20, 20)];
[path addLineToPoint:CGPointMake(self.frame.size.width - 40, 20)];
[path stroke];
這樣就完事了,但是這樣的結(jié)果就是第二張圖所示稼虎,所以我們?yōu)榱诉_(dá)到圖一的效果就要用到:
-(void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint;
這個(gè)函數(shù)衅檀,這是創(chuàng)建二次貝塞爾曲線的函數(shù)
參數(shù):endPoint->終點(diǎn)
controlPoint->控制點(diǎn)
如圖:
但是有一個(gè)問(wèn)題就是控制點(diǎn)怎么獲取,控制點(diǎn)我這里的做法就是取前一個(gè)點(diǎn)和當(dāng)前點(diǎn)的中點(diǎn):
取中點(diǎn)函數(shù)
static CGPoint midpoint(CGPoint p0, CGPoint p1) {
return (CGPoint) {
(p0.x + p1.x) / 2.0,
(p0.y + p1.y) / 2.0
};
}
核心代碼
在 (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event霎俩;
這個(gè)方法中哀军,獲取開始的點(diǎn),賦值給定義的一個(gè)全局 CGPoint previousPoint
在 (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
這個(gè)函數(shù)中獲取滑動(dòng)中的點(diǎn)
UITouch *myTouche = [touches anyObject];
CGPoint point = [myTouche locationInView:self];
//取中點(diǎn)
CGPoint midPoint = midpoint(previousPoint,point);
//用定義過(guò)的UIBezierPath對(duì)象 去加載二次貝塞爾曲線
[self.bezier addQuadCurveToPoint:midPoint controlPoint:previousPoint];
//再將當(dāng)前點(diǎn)賦值給 previousPoint打却,這樣就能連貫的畫出線來(lái)了
previousPoint = point;
//重繪界面
[self setNeedsDisplay];
項(xiàng)目中同時(shí)杉适,還包含:撤銷、恢復(fù)学密、清除淘衙、返回、保存的功能腻暮。
這些功能的主要實(shí)現(xiàn)邏輯就是彤守,將畫好的貝塞爾曲線放到數(shù)組中,然后繪制哭靖,當(dāng)撤銷的時(shí)候就刪除最后一個(gè)具垫,放到另一個(gè)恢復(fù)數(shù)組中,清除的話就清除兩個(gè)數(shù)組试幽,重新繪制一下就行了筝蚕。
完整代碼:
#import <UIKit/UIKit.h>
@interface DrawView : UIView
-(instancetype)initWithPenWidth:(CGFloat )penWidth PenColor:(UIColor *)penColor;
/**
撤銷
*/
-(void)undoAction;
/**
恢復(fù)
*/
-(void)recoverAction;
/**
清除
*/
- (void)clearAction;
@end
#import "DrawView.h"
NSString *const PARAM_PEN_WIDTH = @"penWidth";
NSString *const PARAM_PEN_COLOR = @"penColor";
static CGPoint midpoint(CGPoint p0, CGPoint p1) {
return (CGPoint) {
(p0.x + p1.x) / 2.0,
(p0.y + p1.y) / 2.0
};
}
@interface DrawView ()
{
CGPoint previousPoint;
}
/**`
線的顏色
*/
@property (nonatomic,strong) UIColor *lineColor;
/**
線的寬度
*/
@property (nonatomic,assign) CGFloat lineWidth;
//聲明貝塞爾曲線
@property(nonatomic, strong) UIBezierPath *bezier;
//存儲(chǔ)Undo出來(lái)的線條
@property(nonatomic, strong) NSMutableArray *cancleArray;
//用來(lái)記錄已有線條
@property (nonatomic, strong) NSMutableArray *allLine;
@end
@implementation DrawView
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor whiteColor];
self.allLine = [NSMutableArray arrayWithCapacity:1];
self.cancleArray = [NSMutableArray arrayWithCapacity:50];
}
return self;
}
/**
撤銷
*/
-(void)undoAction{
if (self.allLine.count > 0)
{
NSInteger index = self.allLine.count - 1;
[self.cancleArray addObject:self.allLine[index]];
[self.allLine removeObjectAtIndex:index];
[self setNeedsDisplay];
}
}
/**
恢復(fù)
*/
-(void)recoverAction{
if (self.cancleArray.count > 0)
{
NSInteger index = self.cancleArray.count - 1;
[self.allLine addObject:self.cancleArray[index]];
[self.cancleArray removeObjectAtIndex:index];
[self setNeedsDisplay];
}
}
/**
清除
*/
- (void)clearAction{
[self.allLine removeAllObjects];
[self.cancleArray removeAllObjects];
[self setNeedsDisplay];
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
//新建貝塞斯曲線
self.bezier = [UIBezierPath bezierPath];
//獲取觸摸的點(diǎn)
UITouch *myTouche = [touches anyObject];
CGPoint point = [myTouche locationInView:self];
//把剛觸摸的點(diǎn)設(shè)置為bezier的起點(diǎn)
[self.bezier moveToPoint:point];
previousPoint = point;
//把每條線存入字典中
NSMutableDictionary *tempDic = [[NSMutableDictionary alloc] initWithCapacity:3];
[tempDic setObject:self.lineColor forKey:PARAM_PEN_COLOR];
[tempDic setObject:[NSNumber numberWithFloat:self.lineWidth] forKey:PARAM_PEN_WIDTH];
[tempDic setObject:self.bezier forKey:@"line"];
//把線加入數(shù)組中
[self.allLine addObject:tempDic];
}
-(void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
UITouch *myTouche = [touches anyObject];
CGPoint point = [myTouche locationInView:self];
CGPoint midPoint = midpoint(previousPoint,point);
[self.bezier addQuadCurveToPoint:midPoint controlPoint:previousPoint];
previousPoint = point;
//重繪界面
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect
{
//對(duì)之前的線的一個(gè)重繪過(guò)程
for (int i = 0; i < self.allLine.count; i ++)
{
NSDictionary *tempDic = self.allLine[i];
UIColor *color = tempDic[PARAM_PEN_COLOR];
CGFloat width = [tempDic[PARAM_PEN_WIDTH] floatValue];
UIBezierPath *path = tempDic[@"line"];
[color setStroke];
[path setLineWidth:width];
[path stroke];
}
}
@end