iOS開發(fā)-一個簡單的自定義柱狀圖(可擴展)

首先,我們先來看一下實現(xiàn)的效果圖:


柱狀圖

從整體上來看缓升,柱狀圖其實就是一個自定義的UIView吧史,在view中添加了相關(guān)的子View對象邮辽。針對代碼層面來說的話,代碼中的注釋都很詳細贸营,簡顯易懂吨述,這里就不做解釋了。請直接看代碼:
LGJHistogramView.h

//
//  LGJHistogramView.h
//  LGJDemo
//
//  Created by ADIQueen on 2019/8/9.
//  Copyright ? 2019年 com.harego. All rights reserved.
//

#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

typedef void(^HistogramViewBlock)(NSInteger inde);

@interface LGJHistogramView : UIView

@property(nonatomic,assign)float pillarsWidth;//柱子的寬度 默認是18pt

@property(nonatomic,assign)float pillarsNumber;//柱子數(shù)量  默認0

@property(nonatomic,assign)float pillarsMargin;//柱子間距 默認26

@property(nonatomic,assign)NSInteger vertVersion; //縱向等分數(shù)量 默認5

@property(nonatomic,strong)NSString *saidMeaning;//柱狀圖的表示含義 例如:分數(shù)---成績---得分  默認是分數(shù)

@property(nonatomic,assign)BOOL showleftLine;//是否顯示左側(cè)邊

@property(nonatomic,strong)UIColor *bottomTextColor;//底部文字顏色

@property(nonatomic,strong)UIColor *leftTextColor;//左側(cè)文字顏色

@property(nonatomic,strong)UIColor *pillarsTopTextColor;//柱子上面具體的小數(shù)字的顏色

@property(nonatomic,assign)BOOL showPillarsTopText;//是否顯示柱子上面具體的小數(shù)字 默認YES

/**
 柱子的點擊事件
 */
@property(nonatomic,copy)HistogramViewBlock clickBlock;

/**
 數(shù)據(jù)容器
 */
@property(nonatomic,strong)NSArray *keyValues;


@end

NS_ASSUME_NONNULL_END

LGJHistogramView.m

//
//  LGJHistogramView.m
//  LGJDemo
//
//  Created by ADIQueen on 2019/8/9.
//  Copyright ? 2019年 com.harego. All rights reserved.
//

#import "LGJHistogramView.h"
#import "LGJTableData.h"

#define Left_Margin 34
#define Right_Margin 16

//RGB顏色
#define colorWithRGB(r, g, b) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:1.0]
#define colorWithRGBAlpha(r, g, b, a) [UIColor colorWithRed:(r)/255.0 green:(r)/255.0 blue:(r)/255.0 alpha:a]
#define colorWithRGBValue(rgbValue) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 green:((float)((rgbValue & 0xFF00) >> 8))/255.0 blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]
#define colorWithSameRGB(rgb) [UIColor colorWithRed:(rgb)/255.0 green:(rgb)/255.0 blue:(rgb)/255.0 alpha:1.0]


@interface LGJHistogramView ()

@property(nonatomic,assign)float rowSpace;//行距

@property(nonatomic,assign)float bigestNumber;//最大值

@property(nonatomic,assign)float con_pillars_margin;//底部文字距離柱子間距 默認是8

/**
 下面的三個屬性都不需要設(shè)置 都是根數(shù)傳入的數(shù)據(jù)源獲取的
 */
@property(nonatomic,strong)NSMutableArray *ys; //y軸坐標 [0,2,4,6,8,10]

@property(nonatomic,strong)NSArray *xs; //x軸坐標 默認為空

@property(nonatomic,strong)NSArray *numbers; //y軸實際值 默認為空

@end

@implementation LGJHistogramView

//初始化
-(instancetype)initWithFrame:(CGRect)frame{
    
    if (self = [super initWithFrame:frame]) {
        
        _vertVersion = 5;
        
        _pillarsWidth = 18;
        
        _pillarsMargin = 26;
        
        _con_pillars_margin = 8;
        
        _showPillarsTopText = YES;
        
    }
    return self;
}

//柱狀圖的繪制
- (void)drawRect:(CGRect)rect {
    
    //創(chuàng)建畫布
    CGContextRef ctr = UIGraphicsGetCurrentContext();
    
    [colorWithRGB(235, 235, 235) set];
    
    CGContextSetLineWidth(ctr, 1);
    
    //橫向分隔線
    for (NSInteger index = 0; index < 6; index++) {
        CGContextMoveToPoint(ctr, Left_Margin, _rowSpace * (index + 2));
        CGContextAddLineToPoint(ctr, self.frame.size.width - Left_Margin - Right_Margin, _rowSpace * (index + 2));
        CGContextStrokePath(ctr);
    }
    
    //左側(cè)邊
    if (self.showleftLine) {
        
        CGContextMoveToPoint(ctr, Left_Margin, _rowSpace * 4 / 3.0);
        CGContextAddLineToPoint(ctr, Left_Margin, _rowSpace * ((_vertVersion + 2)));
        CGContextStrokePath(ctr);
        
    }
    
    
    //繪制分數(shù)分割數(shù)字
    NSMutableParagraphStyle *paragraph = [[NSMutableParagraphStyle alloc] init];
    NSDictionary *attribute = @{NSFontAttributeName:[UIFont systemFontOfSize:12],NSParagraphStyleAttributeName:paragraph,NSForegroundColorAttributeName:colorWithRGBValue(0x9b9b9b)};
    
    for (NSInteger inde = 0; inde < self.ys.count; inde++) {
        NSString *scaleValue = self.ys[(self.ys.count - 1) - inde];
        paragraph.alignment = NSTextAlignmentCenter;
        
        if (_leftTextColor) {
            NSDictionary *attribute1 = @{NSFontAttributeName:[UIFont systemFontOfSize:12],NSParagraphStyleAttributeName:paragraph,NSForegroundColorAttributeName:_leftTextColor};
            [scaleValue drawInRect:CGRectMake(0, _rowSpace * (inde + 1) + _rowSpace / 2, Left_Margin, _rowSpace) withAttributes:attribute1];
        }else{
            [scaleValue drawInRect:CGRectMake(0, _rowSpace * (inde + 1) + _rowSpace / 2, Left_Margin, _rowSpace) withAttributes:attribute];
        }
    }
    
    //繪制分數(shù)兩個字
    if (_saidMeaning) {
        [_saidMeaning drawInRect:CGRectMake(Left_Margin / 4, _rowSpace / 1.5 , Left_Margin, _rowSpace) withAttributes:attribute];
    }else{
        [@"分數(shù)" drawInRect:CGRectMake(Left_Margin / 4, _rowSpace / 1.5 , Left_Margin, _rowSpace) withAttributes:attribute];
    }
    
    //繪制柱子
    for (NSInteger index = 0; index < self.xs.count; index++) {
        
        CGFloat number = [self.numbers[index] floatValue];
        
        CGFloat scale =  number / _bigestNumber;
        
        UIButton *barBtn = [UIButton buttonWithType:UIButtonTypeCustom];
        
        CGFloat x = _pillarsMargin * (index + 1) + _pillarsWidth * (index) + Left_Margin / 2.0;
        CGFloat height = _rowSpace * _vertVersion * scale;
        CGFloat y = _rowSpace * (_vertVersion + 2) - height;
        CGFloat width = _pillarsWidth;
        
        
        barBtn.frame = CGRectMake(x,  _rowSpace * _vertVersion + _rowSpace * 2, _pillarsWidth, 0);
        barBtn.tag = index;
        [self addSubview:barBtn];
        [barBtn addTarget:self action:@selector(barChartClick:) forControlEvents:UIControlEventTouchUpInside];
        [UIView animateWithDuration:1.0 animations:^{
            barBtn.frame = CGRectMake(x, y, width, height);
            barBtn.backgroundColor = [self colorWithIndex:(int)index];
        }];
        
        //橫向文字
        NSString *itemStr = self.xs[index];
        
        if (_bottomTextColor) {
            NSDictionary *attribute1 = [attribute copy];
            attribute1 = @{NSFontAttributeName:[UIFont systemFontOfSize:12],NSParagraphStyleAttributeName:paragraph,NSForegroundColorAttributeName:_bottomTextColor};
            [itemStr drawInRect:CGRectMake(_pillarsMargin * index + _pillarsMargin / 2.0 + _pillarsWidth * index + + Left_Margin / 2.0, _rowSpace * (_vertVersion + 2) + _con_pillars_margin ,_pillarsMargin + _pillarsWidth ,_rowSpace) withAttributes:attribute1];
        }else{
            [itemStr drawInRect:CGRectMake(_pillarsMargin * index + _pillarsMargin / 2.0 + _pillarsWidth * index + + Left_Margin / 2.0, _rowSpace * (_vertVersion + 2) + _con_pillars_margin ,_pillarsMargin + _pillarsWidth ,_rowSpace) withAttributes:attribute];
        }
        
        // 分數(shù)
        if (_showPillarsTopText) {
            if (_pillarsTopTextColor) {
               attribute = @{NSFontAttributeName:[UIFont systemFontOfSize:12],NSParagraphStyleAttributeName:paragraph,NSForegroundColorAttributeName:_pillarsTopTextColor};
            }
            NSString *actureScore = self.numbers[index];
            //繪制每個item的得分
            [actureScore drawInRect:CGRectMake(_pillarsMargin * index + _pillarsMargin / 2.0 + _pillarsWidth * index + Left_Margin / 2.0, y - _rowSpace / 2.0 ,_pillarsMargin + _pillarsWidth ,_rowSpace) withAttributes:attribute];
        }
        
    }
}

/**
 獲取臨接柱子的顏色
 */
-(UIColor *)colorWithIndex:(int)index{
    UIColor *color;
    NSInteger num = index % 7;
    switch (num) {
        case 0:
            color = colorWithRGB(122, 174, 235);
            break;
        case 1:
            color = colorWithRGB(130, 224, 181);
            break;
        case 2:
            color = colorWithRGB(235, 195, 122);
            break;
        case 3:
            color = colorWithRGB(122, 174, 235);
            break;
        case 4:
            color = colorWithRGB(236, 145, 143);
            break;
        case 5:
            color = colorWithRGB(235, 195, 122);
            break;
        default:
            color = colorWithRGB(130, 224, 181);
            break;
    }
    return color;
}


/**
 有的時候一些要求柱子能夠點擊 這個方法是點擊柱子的回調(diào)
 方法
 */
-(void)barChartClick:(UIButton *)button{
    
    if (self.clickBlock) {
        
        self.clickBlock(button.tag);
        
    }
    
}


#pragma mark------setter getter

-(void)setShowleftLine:(BOOL)showleftLine{
    
    _showleftLine = showleftLine;
    
}

-(void)setSaidMeaning:(NSString *)saidMeaning{
    
    _saidMeaning = saidMeaning;
    
}

-(void)setBottomTextColor:(UIColor *)bottomTextColor{
    
    _bottomTextColor = bottomTextColor;
}

-(void)setLeftTextColor:(UIColor *)leftTextColor{
    
    _leftTextColor = leftTextColor;
}

-(void)setShowPillarsTopText:(BOOL)showPillarsTopText{
    
    _showPillarsTopText = showPillarsTopText;
}


-(void)setPillarsTopTextColor:(UIColor *)pillarsTopTextColor{
    
    _pillarsTopTextColor = pillarsTopTextColor;
}


-(void)setKeyValues:(NSArray *)keyValues{
    
    _keyValues = keyValues;
    
    _pillarsNumber = [self.xs count];
    
    _rowSpace = self.frame.size.height /(_vertVersion + 3.0);
    
    _bigestNumber = [self getMaxFloatNumberFrom:self.numbers];
    
    if (_bigestNumber == 0) {
        
        _bigestNumber = 10;
        
    }
    
    CGFloat maxWidth = [self getMaxContentWidth:self.xs];
    
    if (maxWidth <= _pillarsWidth) {
        
        _pillarsMargin = _pillarsWidth + 10;
        
    }else{
        
        _pillarsMargin = maxWidth;
        
    }
}

/**
 給定一組數(shù)據(jù) 求出數(shù)據(jù)中的最大值 最大值返回值是一個整數(shù)
 因為在此縱軸的分割以整數(shù)為單位
 */
-(CGFloat)getMaxFloatNumberFrom:(NSArray *)numbers{
    //和上面這個方法一樣 只不過返回了 浮點數(shù)
    //因為刻畫縱向刻度整數(shù) 而繪制柱子則要的就是精確一點的數(shù)字
    
    CGFloat max = [numbers[0] floatValue];
    
    for (NSInteger inde = 1; inde < numbers.count; inde++) {
        
        CGFloat number = [numbers[inde] floatValue];
        
        if (number > max) {
            
            max = number;
        }
    }
    return max;
}


/**
 給定一組字符串 根據(jù)字符串中的內(nèi)容獲取字符串的最大寬度
 整體上各個柱子之間的間距以字符串的最大寬度為基準
 */
-(CGFloat)getMaxContentWidth:(NSArray *)contents{
    
    CGFloat maxWidth = 0.0;
    
    for (NSInteger inde= 0; inde < contents.count ; inde++) {
        
        NSString *str = contents[inde];
        
        CGSize sizeToFit = [str boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, _rowSpace) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:12.0]} context:nil].size;
        
        if (sizeToFit.width > maxWidth) {
            
            maxWidth = sizeToFit.width;
        }
    }
    return maxWidth;
}



#pragma mark------Lazy Loading

-(NSArray *)xs{
    
    if (!_xs) {
        
        NSMutableArray *array = [NSMutableArray array];
        
        for (NSInteger inde = 0; inde < self.keyValues.count; inde++) {
            
            LGJTableData *data = self.keyValues[inde];
            
            [array addObject:data.itemStr];
            
        }
        _xs = [array copy];
    }
    return _xs;
    
}

-(NSMutableArray *)ys{
    
    if (!_ys) {
        
        _ys = [NSMutableArray arrayWithObjects:@"0", nil];
        
        NSMutableArray *array = [NSMutableArray array];
        
        for (NSInteger inde = 0; inde < self.keyValues.count; inde++) {
            
            LGJTableData *data = self.keyValues[inde];
            
            [array addObject:data.itemValue];
            
        }
        
        NSInteger max = [self getMaxFloatNumberFrom:[array copy]];
        
        //每個縱格的數(shù)字之差
        CGFloat difference = max / _vertVersion;
        
        for (NSInteger ind = 0; ind < _vertVersion; ind++) {
            
            [_ys addObject:[NSString stringWithFormat:@"%ld",(NSInteger)((ind + 1) * difference)]];
        }
    }
    return _ys;
}


-(NSArray *)numbers{
    
    if (!_numbers) {
        
        NSMutableArray *array = [NSMutableArray array];
        
        for (NSInteger inde = 0; inde < self.keyValues.count; inde++) {
            
            LGJTableData *data = self.keyValues[inde];
            
            [array addObject:data.itemValue];
            
        }
        _numbers = [array copy];
    }
    return _numbers;
    
}
@end

//LGJTableData.h

//
//  LGJTableData.h
//  LGJDemo
//
//  Created by ADIQueen on 2019/8/11.
//  Copyright ? 2019年 com.harego. All rights reserved.
//

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface LGJTableData : NSObject

@property(nonatomic,copy)NSString *itemStr; //橫向選項

@property(nonatomic,copy)NSString *itemValue;//縱向?qū)嶋H值

@end

NS_ASSUME_NONNULL_END

//以上的話是具體的實現(xiàn)代碼钞脂,那么如果我們要在某個地方用的話 也是非常的方便揣云。下面舉一個簡單的應用例子:

-(void)addSubView{
    
    
    _histogramView = [[LGJHistogramView alloc]initWithFrame:CGRectMake(0, 100, self.view.frame.size.width, 300)];
    _histogramView.backgroundColor = [UIColor whiteColor];
    
    /*
     做對應的屬性設(shè)置 這里省略了。冰啃。邓夕。。
     */
    
    LGJTableData *data = [LGJTableData new];
    data.itemStr = @"姑姑";
    data.itemValue = @"7";
    LGJTableData *data1 = [LGJTableData new];
    data1.itemStr = @"嘎嘎嘎";
    data1.itemValue = @"10";
    LGJTableData *data2 = [LGJTableData new];
    data2.itemStr = @"哈哈哈哈哈";
    data2.itemValue = @"9";
    NSArray *array = @[data,data1,data2];
    
    _histogramView.keyValues = array; //傳入一個模型數(shù)組阎毅,作為頁面繪制的數(shù)據(jù)源
    
    [self.view addSubview:_histogramView];
    
}

跑起來程序 就是開篇的圖片效果翎迁。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市净薛,隨后出現(xiàn)的幾起案子汪榔,更是在濱河造成了極大的恐慌,老刑警劉巖肃拜,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件痴腌,死亡現(xiàn)場離奇詭異,居然都是意外死亡燃领,警方通過查閱死者的電腦和手機士聪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來猛蔽,“玉大人剥悟,你說我怎么就攤上這事灵寺。” “怎么了区岗?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵略板,是天一觀的道長。 經(jīng)常有香客問我慈缔,道長叮称,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任藐鹤,我火速辦了婚禮瓤檐,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好咆贬,可當我...
    茶點故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著谴古,像睡著了一般。 火紅的嫁衣襯著肌膚如雪悄窃。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天蹂窖,我揣著相機與錄音轧抗,去河邊找鬼。 笑死瞬测,一個胖子當著我的面吹牛横媚,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播月趟,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼灯蝴,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了孝宗?” 一聲冷哼從身側(cè)響起穷躁,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎因妇,沒想到半個月后问潭,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡婚被,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年狡忙,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片址芯。...
    茶點故事閱讀 38,566評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡灾茁,死狀恐怖窜觉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情北专,我是刑警寧澤禀挫,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站逗余,受9級特大地震影響特咆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜录粱,卻給世界環(huán)境...
    茶點故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一腻格、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧啥繁,春花似錦菜职、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至适室,卻和暖如春嫡意,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背捣辆。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工蔬螟, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人汽畴。 一個月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓旧巾,卻偏偏與公主長得像,于是被迫代替她去往敵國和親忍些。 傳聞我的和親對象是個殘疾皇子鲁猩,可洞房花燭夜當晚...
    茶點故事閱讀 43,440評論 2 348

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