iOS 贝塞尔曲线 画线 锯齿问题解决

最近在做一个签字版的模块,需要用到了贝塞尔曲线来画。其实,做这种画板有很多方法,可以用UIGraphics来画,也可以用OPenGL来画,只是当时选择了贝塞尔曲线,没想到还入了坑。
  看下面两张图:


iOS 贝塞尔曲线 画线 锯齿问题解决_第1张图片
不圆滑的线
iOS 贝塞尔曲线 画线 锯齿问题解决_第2张图片
圆滑的线

明显的能够看到下图要比上图圆滑好看一些。

因为我们再使用贝塞尔曲线的时候一般是:

UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(20, 20)]; 
[path addLineToPoint:CGPointMake(self.frame.size.width - 40, 20)];
[path stroke];

这样就完事了,但是这样的结果就是第二张图所示,所以我们为了达到图一的效果就要用到:

-(void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint;

这个函数,这是创建二次贝塞尔曲线的函数
参数:endPoint->终点    
   controlPoint->控制点
如图:

iOS 贝塞尔曲线 画线 锯齿问题解决_第3张图片
二次贝塞尔

但是有一个问题就是控制点怎么获取,控制点我这里的做法就是取前一个点和当前点的中点:

取中点函数
static CGPoint midpoint(CGPoint p0, CGPoint p1) {
    return (CGPoint) {
        (p0.x + p1.x) / 2.0,
        (p0.y + p1.y) / 2.0
    };
}

核心代码

在  (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
这个方法中,获取开始的点,赋值给定义的一个全局 CGPoint previousPoint

在  (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
这个函数中获取滑动中的点
UITouch *myTouche = [touches anyObject];
CGPoint point = [myTouche locationInView:self];
//取中点
CGPoint midPoint = midpoint(previousPoint,point);
//用定义过的UIBezierPath对象 去加载二次贝塞尔曲线
[self.bezier addQuadCurveToPoint:midPoint controlPoint:previousPoint];
//再将当前点赋值给  previousPoint,这样就能连贯的画出线来了
previousPoint = point;
//重绘界面
[self setNeedsDisplay];

项目中同时,还包含:撤销、恢复、清除、返回、保存的功能。

这些功能的主要实现逻辑就是,将画好的贝塞尔曲线放到数组中,然后绘制,当撤销的时候就删除最后一个,放到另一个恢复数组中,清除的话就清除两个数组,重新绘制一下就行了。

完整代码:

#import 

@interface DrawView : UIView


-(instancetype)initWithPenWidth:(CGFloat )penWidth PenColor:(UIColor *)penColor;

/**
 撤销
 */
-(void)undoAction;
/**
 恢复
 */
-(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;
//存储Undo出来的线条
@property(nonatomic, strong) NSMutableArray *cancleArray;
//用来记录已有线条
@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];
    }
}

/**
 恢复
 */
-(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 *)touches withEvent:(UIEvent *)event{
    
    //新建贝塞斯曲线
    self.bezier = [UIBezierPath bezierPath];
    
    //获取触摸的点
    UITouch *myTouche = [touches anyObject];
    CGPoint point = [myTouche locationInView:self];
    
    //把刚触摸的点设置为bezier的起点
    [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"];
    
    //把线加入数组中
    [self.allLine addObject:tempDic];
}
-(void)touchesMoved:(NSSet *)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
{
    //对之前的线的一个重绘过程
    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

你可能感兴趣的:(iOS 贝塞尔曲线 画线 锯齿问题解决)