iOS雷達(dá)圖

最近公司要做一個醫(yī)學(xué)考試類的App遇汞,里面有一個能力分析的雷達(dá)圖最筒,leader讓我來封裝這個雷達(dá)圖颜凯。這個模塊是在做測心率模塊期間完成的蹲缠,也是使用的CoreGraphics來實現(xiàn)的。


之前并不知道雷達(dá)圖長什么樣唉侄,這個是我在百度上下載的咒吐。大概就長這個樣子,我們要用的和這個不太一樣,沒有文字和介紹恬叹。

思路

寫這個的時候候生,沒有查閱什么資料,所以不太清楚別人是怎么做的绽昼。
我是從角度出發(fā)的唯鸭,例如上面的圖中,有5個能力模塊硅确,5個能力模塊的分支(后面就叫它主干了目溉,我也不知道叫什么)兩兩的夾角都是相同的。

  1. 首先把一個主干放在y軸上菱农,這個主干繞原點 O一周是,被5個主干5等分缭付,那么每一個夾角都是2π/5
  2. 主干的長是相同的循未,可以通過正弦余弦來求出每個主干落點的坐標(biāo),再從原點O落點畫線蛉腌。
  3. 之后的網(wǎng)格和能力分布的畫法也是這個思路,同樣是從y軸上的主干取點只厘,不同的是斜邊的長度會根據(jù)網(wǎng)格間距能力百分比變化烙丛,如下圖:

不知道大家還記不記得正弦和余弦是啥了,我是已經(jīng)還給數(shù)學(xué)老師了(雖然初中數(shù)學(xué)也是年組第一的)羔味,剛好寫這個的前一天在看高數(shù)河咽。。

  正弦: sin? = 對邊/斜邊
  余弦: cos? = 臨邊/斜邊

3實現(xiàn)

1. 初始化赋元,定義幾個屬性后面使用
// 元素數(shù)組
@property (strong, nonatomic) NSArray <Element *>*elements;

這是一個放Model的數(shù)組忘蟹,用來儲存能力模塊信息

// 每一個主干的長度
static float element_w = 0;
// 主干到View預(yù)留的空白部分寬度
static float border_w  = 5;
// 中心點的橫坐標(biāo)
static float center_w  = 0;
// 中心點的縱坐標(biāo)
static float center_h  = 0;

- (instancetype)initWithFrame:(CGRect)frame Elements:(NSArray <Element *>*)elements {
    if (self = [super initWithFrame:frame]) {
        self.backgroundColor = [UIColor whiteColor];
        self.elements = elements;
        if (self.frame.size.width>self.frame.size.height)
            element_w = (self.frame.size.height-border_w*2)/2;
        else
            element_w = (self.frame.size.width-border_w*2)/2;
        
        center_w = self.frame.size.width/2;
        center_h = self.frame.size.height/2;

    }
    return self;
}
2.畫主干

- (void)buildRadarMap {
    if (self.elements.count<3) return;
    
    // 獲取畫布
    CGContextRef context = UIGraphicsGetCurrentContext();
    // 劃線顏色
    if (self.trunkLineColor)
        CGContextSetStrokeColorWithColor(context, self.trunkLineColor.CGColor);
    else
        CGContextSetStrokeColorWithColor(context, [UIColor blackColor].CGColor);
    // 劃線寬度
    if (self.lineWidth)
        CGContextSetLineWidth(context, self.lineWidth);
    else
        CGContextSetLineWidth(context, 1);
    // 起點坐標(biāo)
    CGContextMoveToPoint(context, center_w, center_h);
    // 第一條線
    CGContextAddLineToPoint(context, center_w, center_h - element_w);
    
    //畫元素主干
    for (int i = 1; i <self.elements.count; i++) {
        float x   = 0;
        float y   = 0;
        double pi = (M_PI*2.0/(self.elements.count))*i;
        
        // 計算主干落點坐標(biāo)
        Coordinate(pi, element_w, center_w, center_h,&x, &y);
        // 設(shè)置每次的初始點坐標(biāo)
        CGContextMoveToPoint(context, center_w, center_h);
        // 設(shè)置終點坐標(biāo)
        CGContextAddLineToPoint(context, x, y);
    }
    CGContextStrokePath(context);
}

注:self.trunkLineColor是我設(shè)置的一個外部可控主干的顏色,如果尾部沒有設(shè)置就給默認(rèn)值搁凸。self.lineWidth就是先的寬度了媚值。

/**
 *  雷達(dá)圖分成幾等份 默認(rèn)1等份(最外面一圈的框框)
 */
@property (assign, nonatomic) float part;

/**
 *  雷達(dá)線的寬度 默認(rèn)1
 */
@property (assign, nonatomic) float lineWidth;

/**
 *  主干線顏色 默認(rèn)黑色
 */
@property (strong, nonatomic) UIColor *trunkLineColor;

/**
 *  分等份線顏色 默認(rèn)黑色
 */
@property (strong, nonatomic) UIColor *partLineColor;

/**
 *  比例線顏色 默認(rèn)綠色
 */
@property (strong, nonatomic) UIColor *percentLineColor;

- (void)setTrunkLineColor:(UIColor *)trunkLineColor {
    _trunkLineColor =trunkLineColor;
    [self setNeedsDisplay];
}

- (void)setPartLineColor:(UIColor *)partLineColor {
    _partLineColor = partLineColor;
    [self setNeedsDisplay];
}

- (void)setPercentLineColor:(UIColor *)percentLineColor {
    _percentLineColor = percentLineColor;
    [self setNeedsDisplay];
}

設(shè)置set方法調(diào)用setNeedsDisplay外部設(shè)置顏色的時候會調(diào)用drawRect:

3.計算落點的方法
#pragma mark - 算落點坐標(biāo)

void Coordinate (double pi, float l, float c_w , float c_h, float *x, float *y) {
    
    if (pi < M_PI_2) {
        *x = c_w + sin(pi)*l;
        *y = c_h - cos(pi)*l;
    } else if (pi == M_PI_2) {
        *x = c_w + sin(pi)*l;
        *y = c_h + cos(pi)*l;
    } else if (pi < M_PI) {
        *x = c_w + sin(pi)*l;
        *y = c_h - cos(pi)*l;
    } else if (pi == M_PI) {
        *x = c_w + sin(pi)*l;
        *y = c_h + cos(pi)*l;
    } else if (pi < M_PI_2*3) {
        *x = c_w + sin(pi)*l;
        *y = c_h - cos(pi)*l;
    } else if (pi == M_PI_2*3) {
        *x = c_w + sin(pi)*l;
        *y = c_h + cos(pi)*l;
    } else {
        *x = c_w + sin(pi)*l;
        *y = c_h - cos(pi)*l;
    }
}

變量 用途
pi 角度
l 主干長度
c_w 原點橫坐標(biāo)
c_h 原點縱坐標(biāo)
x 落點橫坐標(biāo)
y 落點縱坐標(biāo)

坐標(biāo)系和View的Rect還不太一樣,比如在坐標(biāo)系第二护糖、三象限的y是負(fù)值褥芒,而我們要的坐標(biāo)的y是越往下越大,所以我們要c_h+(-y)嫡良,x同理锰扶。

4.畫網(wǎng)格,也就是要把主干分成幾等分寝受,為了更靈活我也設(shè)置了可以從外部設(shè)置坷牛。
/**
 *  雷達(dá)圖分成幾等份 默認(rèn)1等份(最外面一圈的框框)
 */
@property (assign, nonatomic) float part;

- (void)setPart:(float)part {
    _part = part;
    [self setNeedsDisplay];
}
#pragma mark - 畫雷達(dá)分等分圖

- (void)buildPart {
    
    float r = self.part<=1?1:self.part;

    // 獲取畫布
    CGContextRef context = UIGraphicsGetCurrentContext();
    // 劃線顏色
    if (self.partLineColor)
        CGContextSetStrokeColorWithColor(context, self.partLineColor.CGColor);
    else
        CGContextSetStrokeColorWithColor(context, [UIColor blackColor].CGColor);
    // 劃線寬度
    if (self.lineWidth)
        CGContextSetLineWidth(context, self.lineWidth);
    else
        CGContextSetLineWidth(context, 1);
    // 話分割線
    for (int j = 0; j<r; j++) {
        // 設(shè)置每次的初始點坐標(biāo)
        CGContextMoveToPoint(context, center_w, border_w);
        // 畫百分比分部
        for (int i = 1; i<=self.elements.count; i++) {
            float x   = 0;
            float y   = 0;
            double pi = (M_PI*2.0/(self.elements.count))*i;
            Coordinate(pi,element_w*(r-j)/r, center_w, center_h,&x, &y);
            
            if (i == 1) {
                CGContextMoveToPoint(context, center_w, border_w + element_w*j/r);
            }
            if (i == self.elements.count) {
                CGContextAddLineToPoint(context, center_w, border_w + element_w*j/r);
            } else {
                CGContextAddLineToPoint(context, x, y);
            }
        }

    }
    CGContextStrokePath(context);
}

原理和畫主干是一樣的,只不過是每次的起點變成了上一個點的落點很澄。把主干的長度進(jìn)行了分割

5.畫能力占比線
#pragma mark - 畫百分比占比線

- (void)buildPercent {

    // 獲取畫布
    CGContextRef context = UIGraphicsGetCurrentContext();
    // 劃線顏色
    if (self.percentLineColor)
        CGContextSetStrokeColorWithColor(context, self.percentLineColor.CGColor);
    else
        CGContextSetStrokeColorWithColor(context, [UIColor greenColor].CGColor);
    // 劃線寬度
    if (self.lineWidth)
        CGContextSetLineWidth(context, self.lineWidth);
    else
        CGContextSetLineWidth(context, 1);

    Element *ele = self.elements[0];
    CGContextMoveToPoint(context, center_w, border_w +element_w*(1-ele.percent));
    for (int i = 1; i<=self.elements.count; i++) {
        float x   = 0;
        float y   = 0;
       
        if (i == self.elements.count) {
            //終點京闰,最終回到開始點坐標(biāo)
            Element *ele = self.elements[0];
            CGContextAddLineToPoint(context, center_w, border_w +element_w*(1-ele.percent));
        } else {
            Element *ele = self.elements[i];
            double pi = (M_PI*2.0/(self.elements.count))*i;
            Coordinate(pi,element_w*ele.percent, center_w, center_h,&x, &y);
            CGContextAddLineToPoint(context, x, y);
        }
    }
    CGContextStrokePath(context);
}

這里和畫網(wǎng)格不一樣的就是每次的把主干的長度按照這個能力值百分比分割

6.總結(jié)

因為有之前的心率那得基礎(chǔ)颜及,這里完成的很快,然后做了一個相對比較靈活的封裝蹂楣,還沒有加能力描述和百分比值俏站,后續(xù)加上。
代碼地址:https://github.com/YvanLiu/RadarMap.git
歡迎吐槽捐迫,歡迎指正乾翔,歡迎批評爱葵,歡迎提問施戴。

7.更新

9.23 11:43

重新封裝了一個雷達(dá)圖,這次的會比較全面萌丈。封裝后的效果圖



代碼地址還是之前的


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末赞哗,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子辆雾,更是在濱河造成了極大的恐慌肪笋,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件度迂,死亡現(xiàn)場離奇詭異藤乙,居然都是意外死亡,警方通過查閱死者的電腦和手機惭墓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進(jìn)店門坛梁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人腊凶,你說我怎么就攤上這事划咐。” “怎么了钧萍?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵褐缠,是天一觀的道長。 經(jīng)常有香客問我风瘦,道長队魏,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任万搔,我火速辦了婚禮器躏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蟹略。我一直安慰自己登失,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布挖炬。 她就那樣靜靜地躺著揽浙,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上馅巷,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天膛虫,我揣著相機與錄音,去河邊找鬼钓猬。 笑死稍刀,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的敞曹。 我是一名探鬼主播账月,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼澳迫!你這毒婦竟也來了局齿?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤橄登,失蹤者是張志新(化名)和其女友劉穎抓歼,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體拢锹,經(jīng)...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡谣妻,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了卒稳。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蹋半。...
    茶點故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖展哭,靈堂內(nèi)的尸體忽然破棺而出湃窍,到底是詐尸還是另有隱情,我是刑警寧澤匪傍,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布您市,位于F島的核電站,受9級特大地震影響役衡,放射性物質(zhì)發(fā)生泄漏茵休。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一手蝎、第九天 我趴在偏房一處隱蔽的房頂上張望榕莺。 院中可真熱鬧,春花似錦棵介、人聲如沸钉鸯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽唠雕。三九已至贸营,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間岩睁,已是汗流浹背钞脂。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留捕儒,地道東北人冰啃。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓,卻偏偏與公主長得像刘莹,于是被迫代替她去往敵國和親阎毅。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,612評論 2 350

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