SDDrawView:畫板組件,這里的箭頭很有特點!


SDDrawView簡介


SDDrawView 是一款基于貝塞爾曲線的畫板組件,目前樣式包含線條疚膊、矩形、圓形、箭頭等樣式.具有調(diào)整畫板顏色,線條寬度,線條顏色等基本功能.后期準(zhǔn)備接入圖片涂改,橡皮擦功能,添加文字等功能.至于為什么要做SDDrawView這樣的一個三方畫板組件,其實SDDrawView大部分功能和現(xiàn)在網(wǎng)上的畫板組件都是類似的,是一個不折不扣的造輪子組件.其實,在網(wǎng)上找了很多的畫板組件三方中箭頭樣式令人不是太滿意顯得非常的生硬,大部分是一個矩形加一個三角形組成的多邊形箭頭.SDDrawView的箭頭樣式卻不同,SDDrawView箭頭樣式更類似于QQ截圖中的箭頭,更加的圓滑更加趨近于現(xiàn)實.接下來,看一下SDDrawView的效果演示圖.


SDDrawView快速集成


如何快速集成SDDrawView?非常簡單,只需要把SDDrawViewDemo下載下來,然后把Demo中的SDDrawView文件夾拖到你的工程中,然后如下導(dǎo)入頭文件即可.

#import "SDDrawView.h"

SDDrawView初始化也比較簡單.我們初始化一個SDDrawView對象然后添加到對應(yīng)的View視圖上即可.

//懶加載的形式初始化(可用可不用~)
- (SDDrawView *)drawView{
    
    if(_drawView == nil){
        _drawView = [[SDDrawView alloc] initWithFrame:[UIScreen mainScreen].bounds];
        _drawView.drawViewColor = [UIColor whiteColor];//畫板顏色
        _drawView.lineWidth = 2.0f;//線條寬度
        _drawView.drawStyle = DrawStyleLine;//樣式
        _drawView.lineColor = [UIColor redColor];//線條顏色
    }
    return _drawView;
    
}

圖形樣式選擇是一個枚舉值,只需設(shè)定對應(yīng)的樣式,就可繪制不同的圖形.

typedef enum : NSUInteger {
    DrawStyleLine,
    DrawStyleSquare,
    DrawStyleCircle,
    DrawStyleArrow
} DrawStyle;

這里說明一下SDDrawView所有的屬性和方法.

屬性或者方法 說明
drawViewColor 畫板顏色
lineWidth 畫筆寬度
drawStyle 繪制樣式 值為DrawStyle的枚舉值
lineColor 畫筆顏色
- (void)cleanAction; 清除畫板
- (void)rollbackAction; 回退上一步


SDDrawView原理及箭頭繪制


SDDrawView的核心是使用貝塞爾曲線進(jìn)行的繪制.通過touchesBegan 渐扮、touchesMoved 嫂伞、touchesEnded 三個方法檢測用戶的觸摸點,然后調(diào)用[self setNeedsDisplay];方法進(jìn)行重繪操作.

每一個圖形或者每一條線條都是一個SDBezierPath對象,一個SDBezierPath對象繼承于UIBezierPath,每一個SDBezierPath對象都具有以下的成員變量.

@property(nonatomic,strong) UIColor *lineColor;//繪制顏色
@property(nonatomic,assign) DrawStyle drawStyle;//繪制樣式

@property(nonatomic,assign) CGPoint startPoint;//當(dāng)是矩形或者是圓的時候,需要使用到這個屬性
@property(nonatomic,assign) CGPoint endPoint;//當(dāng)是矩形或者是圓的時候,需要使用到這個屬性

像線條、矩形聂抢、圓形這三種樣式還是非常容易實現(xiàn)的,那么如同下面箭頭形式的是如何實現(xiàn)的呢?

繪制分析圖

這里就使用到了極坐標(biāo)的知識,這里可以去看我寫的3D圖形:矩陣與線性變換里面有一部分寫到2D變換矩陣,如下圖所示.

為了讓各位看官更加直觀的了解旋轉(zhuǎn)矩陣公式,我們可以得知假定一個向量 \vec{u} 的坐標(biāo)為(x,y),那么經(jīng)過旋轉(zhuǎn)的 \theta 角度之后得到新的向量 \vec{u'},那么\vec{u'} 的坐標(biāo)(x',y'),x'和y'分別是多少呢?

假設(shè) 向量\vec{u}與x的夾角為 \phi ,那么 x = \vec{u}\cos{\phi}y = \vec{u}\sin{\phi} (根據(jù)三角函數(shù)即可推導(dǎo)~)

那么我們可知\vec{u'}與x軸之間的夾角為 \phi + \theta,那么就有一下的推導(dǎo)過程
x' = \vec{u}\cos(\phi + \theta) = \vec{u}(\cos\phi\cos\theta - \sin\phi\sin\theta) = x\cos\theta - y\sin\theta
y' = \vec{u}\sin(\phi + \theta) = \vec{u}(\sin\phi\cos\theta + \cos\phi\sin\theta) = x\sin\theta + y\cos\theta

SDDrawView代碼中的體現(xiàn)如下所示.


- (CGPoint)rotateVecWithPx:(float)px py:(float)py ang:(double)ang newLen:(double)newLen{
    
    double vx = px * cos(ang) - py * sin(ang);
    double vy = px * sin(ang) + py * cos(ang);
    double d = sqrt(vx * vx + vy * vy);
    vx = vx / d * newLen;
    vy = vy / d * newLen;
    return CGPointMake((float) vx, (float) vy);
}

上面原理說的真的是好枯燥,那么久說一下實際過程中是如何處理的,我們通過觀看可以知道我們所需要的就是7個點的坐標(biāo)就可完成一個箭頭多邊形圖形的繪制.我們知道箭頭中軸線上的兩個坐標(biāo),也就是用戶開始觸摸和結(jié)束觸摸的兩個點.我們可以假定一下箭頭的長度比例和屬性.例如b和h到中軸線的角度都為30度,長度比例為0.2;c和e到中軸線的角度都為18度,長度比例為0.157;d和f到中軸線的角度都為90度(d和f是相對坐標(biāo)原點o').長度比例為0.023.如下所示.

        //初始化箭頭的相關(guān)固定值
        KEY_POINT_LEN1 = 70;
        KEY_POINT_LEN2 = 55;
        KEY_POINT_LEN3 = 8;
        KEY_POINT_ANGLE1 = 30 * M_PI/ 180;
        KEY_POINT_ANGLE2 = 18 * M_PI/ 180;
        KEY_POINT_ANGLE3 = 90 * M_PI/ 180;
        KEY_POINT_RATIO1 = 0.2;
        KEY_POINT_RATIO2 = 0.157;
        KEY_POINT_RATIO3 = 0.023;

坐標(biāo)系原點的建立說明: 當(dāng)需要知道bced四個點的時候,坐標(biāo)原點就在點a上;當(dāng)需要知道fd兩個點的時候,坐標(biāo)原點就在點o'上.

通過上面的一系列的準(zhǔn)備,接下來我們就可以計算各個點的坐標(biāo)情況了.代碼如下所示.

        //配置箭頭的6個點位
        double len1 = KEY_POINT_LEN1;
        double len2 = KEY_POINT_LEN2;
        double len3 = KEY_POINT_LEN3;
    
        double len = sqrt(pow((endX - startX), 2) + pow((endY - startY), 2));
        if (len * KEY_POINT_RATIO1 < KEY_POINT_LEN1) {
            len1 = len * KEY_POINT_RATIO1;
        }
        if (len * KEY_POINT_RATIO2 < KEY_POINT_LEN2) {
            len2 = len * KEY_POINT_RATIO2;
        }
        if (len * KEY_POINT_RATIO3 < KEY_POINT_LEN3) {
            len3 = len * KEY_POINT_RATIO3;
        }
        CGPoint arrXY_11 = [self rotateVecWithPx:endX - startX py:endY - startY ang:KEY_POINT_ANGLE1 newLen:len1];
        CGPoint arrXY_12 = [self rotateVecWithPx:endX - startX py:endY - startY ang:-KEY_POINT_ANGLE1 newLen:len1];
        CGPoint arrXY_21 = [self rotateVecWithPx:endX - startX py:endY - startY ang:KEY_POINT_ANGLE2 newLen:len2];
        CGPoint arrXY_22 = [self rotateVecWithPx:endX - startX py:endY - startY ang:-KEY_POINT_ANGLE2 newLen:len2];
        CGPoint arrXY_31 = [self rotateVecWithPx:startX - endX py:startY - endY ang:KEY_POINT_ANGLE3 newLen:len3];
        CGPoint arrXY_32 = [self rotateVecWithPx:startX - endX py:startY - endY ang:-KEY_POINT_ANGLE3 newLen:len3];;
        
        //轉(zhuǎn)換到原始坐標(biāo)系中
        float x11 = endX - arrXY_11.x;
        float y11 = endY - arrXY_11.y;
        float x12 = endX - arrXY_12.x;
        float y12 = endY - arrXY_12.y;
        float x21 = endX - arrXY_21.x;
        float y21 = endY - arrXY_21.y;
        float x22 = endX - arrXY_22.x;
        float y22 = endY - arrXY_22.y;
        float x31 = startX - arrXY_31.x;
        float y31 = startY - arrXY_31.y;
        float x32 = startX - arrXY_32.x;
        float y32 = startY - arrXY_32.y;

然后我們只需要把我們的這7個點添加到UIBezierPath對象即可.

        [self moveToPoint:endPoint];
        [self addLineToPoint:CGPointMake(x11, y11)];
        [self addLineToPoint:CGPointMake(x21, y21)];
        [self addLineToPoint:CGPointMake(x32, y32)];
        [self addLineToPoint:CGPointMake(x31, y31)];
        [self addLineToPoint:CGPointMake(x22, y22)];
        [self addLineToPoint:CGPointMake(x12, y12)];


結(jié)束


SDDrawView的分享就到這里了,如果需要歡迎下載收藏點贊. 如果有任何問題歡迎指導(dǎo)批評,我會及時回復(fù)的.最后添加上github的傳送門.感謝查看本篇博客.

→SDDrawView傳送門



?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末钧嘶,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子琳疏,更是在濱河造成了極大的恐慌有决,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件空盼,死亡現(xiàn)場離奇詭異书幕,居然都是意外死亡,警方通過查閱死者的電腦和手機揽趾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進(jìn)店門台汇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人但骨,你說我怎么就攤上這事励七。” “怎么了奔缠?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵掠抬,是天一觀的道長。 經(jīng)常有香客問我校哎,道長两波,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任闷哆,我火速辦了婚禮腰奋,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘抱怔。我一直安慰自己劣坊,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布屈留。 她就那樣靜靜地躺著局冰,像睡著了一般测蘑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上康二,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天碳胳,我揣著相機與錄音,去河邊找鬼沫勿。 笑死挨约,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的产雹。 我是一名探鬼主播诫惭,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼洽故!你這毒婦竟也來了贝攒?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤时甚,失蹤者是張志新(化名)和其女友劉穎隘弊,沒想到半個月后荒适,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體梨熙,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡刀诬,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了陕壹。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片质欲。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖糠馆,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情又碌,我是刑警寧澤九昧,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布毕匀,位于F島的核電站铸鹰,受9級特大地震影響皂岔,放射性物質(zhì)發(fā)生泄漏蹋笼。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一姓建、第九天 我趴在偏房一處隱蔽的房頂上張望诞仓。 院中可真熱鬧缤苫,春花似錦、人聲如沸活玲。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽舒憾。三九已至,卻和暖如春镀迂,著一層夾襖步出監(jiān)牢的瞬間丁溅,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工探遵, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留窟赏,地道東北人。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓箱季,卻偏偏與公主長得像涯穷,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子藏雏,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,781評論 2 354

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