首先,我们先来看一下实现的效果图:
柱状图
从整体上来看,柱状图其实就是一个自定义的UIView,在view中添加了相关的子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;//柱子数量 默认0
@property(nonatomic,assign)float pillarsMargin;//柱子间距 默认26
@property(nonatomic,assign)NSInteger vertVersion; //纵向等分数量 默认5
@property(nonatomic,strong)NSString *saidMeaning;//柱状图的表示含义 例如:分数---成绩---得分 默认是分数
@property(nonatomic,assign)BOOL showleftLine;//是否显示左侧边
@property(nonatomic,strong)UIColor *bottomTextColor;//底部文字颜色
@property(nonatomic,strong)UIColor *leftTextColor;//左侧文字颜色
@property(nonatomic,strong)UIColor *pillarsTopTextColor;//柱子上面具体的小数字的颜色
@property(nonatomic,assign)BOOL showPillarsTopText;//是否显示柱子上面具体的小数字 默认YES
/**
柱子的点击事件
*/
@property(nonatomic,copy)HistogramViewBlock clickBlock;
/**
数据容器
*/
@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
/**
下面的三个属性都不需要设置 都是根数传入的数据源获取的
*/
@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 {
//创建画布
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);
}
//左侧边
if (self.showleftLine) {
CGContextMoveToPoint(ctr, Left_Margin, _rowSpace * 4 / 3.0);
CGContextAddLineToPoint(ctr, Left_Margin, _rowSpace * ((_vertVersion + 2)));
CGContextStrokePath(ctr);
}
//绘制分数分割数字
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];
}
}
//绘制分数两个字
if (_saidMeaning) {
[_saidMeaning drawInRect:CGRectMake(Left_Margin / 4, _rowSpace / 1.5 , Left_Margin, _rowSpace) withAttributes:attribute];
}else{
[@"分数" 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];
}
// 分数
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;
}
/**
有的时候一些要求柱子能够点击 这个方法是点击柱子的回调
方法
*/
-(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;
}
}
/**
给定一组数据 求出数据中的最大值 最大值返回值是一个整数
因为在此纵轴的分割以整数为单位
*/
-(CGFloat)getMaxFloatNumberFrom:(NSArray *)numbers{
//和上面这个方法一样 只不过返回了 浮点数
//因为刻画纵向刻度整数 而绘制柱子则要的就是精确一点的数字
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;
}
/**
给定一组字符串 根据字符串中的内容获取字符串的最大宽度
整体上各个柱子之间的间距以字符串的最大宽度为基准
*/
-(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]];
//每个纵格的数字之差
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;//纵向实际值
@end
NS_ASSUME_NONNULL_END
//以上的话是具体的实现代码,那么如果我们要在某个地方用的话 也是非常的方便。下面举一个简单的应用例子:
-(void)addSubView{
_histogramView = [[LGJHistogramView alloc]initWithFrame:CGRectMake(0, 100, self.view.frame.size.width, 300)];
_histogramView.backgroundColor = [UIColor whiteColor];
/*
做对应的属性设置 这里省略了。。。。
*/
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; //传入一个模型数组,作为页面绘制的数据源
[self.view addSubview:_histogramView];
}
跑起来程序 就是开篇的图片效果。
网友评论