iOS 貝塞爾曲線 畫線 鋸齒問(wèn)題解決

最近在做一個(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
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市铺坞,隨后出現(xiàn)的幾起案子起宽,更是在濱河造成了極大的恐慌,老刑警劉巖济榨,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件坯沪,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡擒滑,警方通過(guò)查閱死者的電腦和手機(jī)腐晾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)丐一,“玉大人藻糖,你說(shuō)我怎么就攤上這事】獬担” “怎么了巨柒?”我有些...
    開封第一講書人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)柠衍。 經(jīng)常有香客問(wèn)我潘拱,道長(zhǎng),這世上最難降的妖魔是什么拧略? 我笑而不...
    開封第一講書人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任芦岂,我火速辦了婚禮,結(jié)果婚禮上垫蛆,老公的妹妹穿的比我還像新娘禽最。我一直安慰自己,他們只是感情好袱饭,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開白布川无。 她就那樣靜靜地躺著,像睡著了一般虑乖。 火紅的嫁衣襯著肌膚如雪懦趋。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,772評(píng)論 1 290
  • 那天疹味,我揣著相機(jī)與錄音仅叫,去河邊找鬼赂蕴。 笑死棋凳,一個(gè)胖子當(dāng)著我的面吹牛忌穿,可吹牛的內(nèi)容都是我干的刃唤。 我是一名探鬼主播,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼坎缭,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼竟痰!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起掏呼,我...
    開封第一講書人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤坏快,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后憎夷,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體莽鸿,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年岭接,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了富拗。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡鸣戴,死狀恐怖啃沪,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情窄锅,我是刑警寧澤创千,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站入偷,受9級(jí)特大地震影響追驴,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜疏之,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一殿雪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧锋爪,春花似錦丙曙、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至拯爽,卻和暖如春索抓,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工逼肯, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留耸黑,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓汉矿,卻偏偏與公主長(zhǎng)得像崎坊,于是被迫代替她去往敵國(guó)和親备禀。 傳聞我的和親對(duì)象是個(gè)殘疾皇子洲拇,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 前言:關(guān)于貝塞爾曲線與CAShapeLayer的學(xué)習(xí) 學(xué)習(xí)Demo演示: 貝塞爾曲線簡(jiǎn)單了解 使用UIBezier...
    麥穗0615閱讀 17,861評(píng)論 18 149
  • 談?wù)勜惾麪柷€ 最近在做項(xiàng)目的時(shí)候,需要用到一個(gè)動(dòng)畫曲尸,非常簡(jiǎn)單的動(dòng)畫赋续,簡(jiǎn)單到就是直接對(duì)一個(gè)View做平移… 然而雖...
    雨潤(rùn)聽潮閱讀 5,981評(píng)論 1 16
  • 貝塞爾曲線開發(fā)的藝術(shù) 一句話概括貝塞爾曲線:將任意一條曲線轉(zhuǎn)化為精確的數(shù)學(xué)公式。 很多繪圖工具中的鋼筆工具另患,就是典...
    eclipse_xu閱讀 27,686評(píng)論 38 370
  • 最近在做項(xiàng)目的時(shí)候纽乱,需要用到一個(gè)動(dòng)畫,非常簡(jiǎn)單的動(dòng)畫昆箕,簡(jiǎn)單到就是直接對(duì)一個(gè)View做平移... 然而雖然動(dòng)畫簡(jiǎn)單鸦列,...
    IAMDAEMON閱讀 4,272評(píng)論 12 69
  • 背景: 給一系列頂點(diǎn),如果只是用直線將其中的各個(gè)點(diǎn)依次連接起來(lái)鹏倘,最終形成一個(gè)折線圖薯嗤,這種很容易實(shí)現(xiàn)。但是現(xiàn)實(shí)...
    狂風(fēng)無(wú)跡閱讀 39,094評(píng)論 12 70