首先,我們先來看一下實現(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];
}
跑起來程序 就是開篇的圖片效果翎迁。