You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

463 lines
16 KiB

//
// FBYLineGraphView.m
// FBYDataDisplay-iOS
//
// Created by fby on 2018/1/18.
// Copyright © 2018FBYDataDisplay-iOS. All rights reserved.
//
#import "FBYLineGraphView.h"
#import "FBYLineGraphContentView.h"
/**
* Y轴刻度标签Y轴 之间 空隙
*/
#define HORIZON_YMARKLAB_YAXISLINE 10.f
/**
* Y轴刻度标签 宽度
*/
#define YMARKLAB_WIDTH 25.f
/**
* Y轴刻度标签 高度
*/
#define YMARKLAB_HEIGHT 16.f
/**
* X轴刻度标签 宽度
*/
#define XMARKLAB_WIDTH 40.f
/**
* X轴刻度标签 高度
*/
#define XMARKLAB_HEIGHT 16.f
/**
* 最上边的Y轴刻度标签距离顶部的 距离
*/
#define YMARKLAB_TO_TOP 12.f
@interface FBYLineGraphView() {
// 数据的 x轴坐标
NSMutableArray *xMarkValues;
// 最大值
NSInteger xMarkMaxValue;
// 最小值
NSInteger xMarkMinValue;
// 值数组
NSMutableArray *valueArray;
}
/// 类型 0: 时间(24小时) 1:日期(7)
@property (nonatomic, assign) int type;
/**
* 表名标签
*/
@property (nonatomic, strong) UILabel *titleLab;
/**
* 显示折线图的可滑动视图
*/
@property (nonatomic, strong) UIScrollView *scrollView;
/**
* 折线图
*/
@property (nonatomic, strong) FBYLineGraphContentView *lineGraphContentView;
/**
* Y轴刻度标签title
*/
@property (nonatomic, strong) NSArray *yMarkTitles;
/**
* X轴坐标 和 对应的折线图点的值
*/
@property (nonatomic, strong) NSArray *xMarkTitlesAndValues;
/// X轴的 刻度数组 (根据方法需要修改成 24小时的 还是 日期制的)
@property (nonatomic, strong) NSMutableArray *xMarkScaleArr;
@end
@implementation FBYLineGraphView
- (void)setXScaleMarkLEN:(CGFloat)xScaleMarkLEN {
_xScaleMarkLEN = xScaleMarkLEN;
}
- (void)setYMarkTitles:(NSArray *)yMarkTitles {
_yMarkTitles = yMarkTitles;
}
- (void)setMaxValue:(CGFloat)maxValue {
_maxValue = maxValue;
}
- (void)setMinValue:(CGFloat)minValue {
_minValue = minValue;
}
- (void)setTitle:(NSString *)title {
_title = title;
}
/**
* 设置折线图显示的数据和对应X坐标轴刻度标签 (时间戳 24小时制)
* @param day 哪一天
* @param xMarkTitlesTimeAndValues 折线图显示的数据
* @param titleKey 标签(如:1585817374 时间戳)
* @param valueKey 数据 (:80)
*/
- (void)setXMarkWithDay:(NSString*)day TitlesTimeAndValues:(NSArray *)xMarkTitlesTimeAndValues titleKey:(NSString *)titleKey valueKey:(NSString *)valueKey{
_xMarkTitlesAndValues = xMarkTitlesTimeAndValues;
xMarkValues = [NSMutableArray array];
valueArray = [NSMutableArray array];
for (NSDictionary *dic in xMarkTitlesTimeAndValues) {
[xMarkValues addObject:[dic objectForKey:titleKey]];
[valueArray addObject:[dic objectForKey:valueKey]];
}
_xMarkScaleArr = [NSMutableArray array];
if(YES){
//等距模式
NSDateFormatter*dateFormatter = [[NSDateFormatter alloc]init];
[dateFormatter setDateFormat:@"yyyy-MM-dd"];
//日期的零点
xMarkMinValue = [dateFormatter dateFromString:day].timeIntervalSince1970;
//日期后一天的零点
xMarkMaxValue = [dateFormatter dateFromString:day].timeIntervalSince1970 + 60*60*24;
for (int i = 0; i < 5; i++) {
//坐标显示的刻度
NSString * xMark = [self getDateFormatWithStr:@"yyyy-MM-dd-H" timeInterval: xMarkMinValue + (60*60*6*i)];
NSString *Y_Str = [xMark componentsSeparatedByString:@"-"].lastObject;
if(i > 0 && [Y_Str isEqualToString:@"0"]){
[_xMarkScaleArr addObject:@"24"];
}else{
[_xMarkScaleArr addObject:Y_Str];
}
}
}else{
// 跨度是多少个小时
long numHour = 24;
//日期的零点
xMarkMinValue = [self getDateFormatWithStr:@"yyyy-MM-dd" WithDateStr:day];
//日期后一天的零点
xMarkMaxValue = xMarkMinValue + 60*60*24;
if(_xMarkTitlesAndValues.count > 0){
//自适应模式
NSNumber *startTime = [xMarkValues firstObject];
NSNumber *endTime = [xMarkValues lastObject];
NSDateFormatter*dateFormatter = [[NSDateFormatter alloc]init];
[dateFormatter setDateFormat:@"yyyy-MM-dd-HH"];
//起始的整点时间 (如果不是偶数点的,需要前面补足一个偶数点)
NSString *startIntegerHour = [self getDateFormatWithStr:@"yyyy-MM-dd-HH" timeInterval: startTime.integerValue];
NSString *tempStartHour = [startIntegerHour componentsSeparatedByString:@"-"].lastObject;
if (([tempStartHour intValue] % 2) != 0){
startIntegerHour = [self getDateFormatWithStr:@"yyyy-MM-dd-HH" timeInterval: startTime.integerValue-60*60];
}
xMarkMinValue = [dateFormatter dateFromString:startIntegerHour].timeIntervalSince1970;
//终止的整点时间 (因为大部分都是几点多的情况,需要补足,补足后如果不是偶数点的,需要再补足一个偶数点,这个不可能超过24点的)
NSString *endIntegerHour = [self getDateFormatWithStr:@"yyyy-MM-dd-HH" timeInterval: endTime.integerValue+60*60];
NSString *tempEndHour = [endIntegerHour componentsSeparatedByString:@"-"].lastObject;
if ( (([tempEndHour intValue] % 2) != 0)
&& ![tempEndHour isEqualToString:@"00"]){
endIntegerHour = [self getDateFormatWithStr:@"yyyy-MM-dd-HH" timeInterval: endTime.integerValue+60*60*2];
}
xMarkMaxValue = [dateFormatter dateFromString:endIntegerHour].timeIntervalSince1970;
numHour = (xMarkMaxValue - xMarkMinValue) / (60*60);
if (numHour < 1) {
numHour = 1;
xMarkMaxValue = xMarkMinValue + 60*60;
}
}
// 每个跨度的间隔时间
NSInteger PerTime = 60*60;
for (int i = 0; i < numHour+1; i++) {
//坐标显示的刻度
NSString * xMark = [self getDateFormatWithStr:@"yyyy-MM-dd-H" timeInterval: xMarkMinValue + (PerTime*i)];
NSString *Y_Str = [xMark componentsSeparatedByString:@"-"].lastObject;
if(i > 0 && [Y_Str isEqualToString:@"0"]){
[_xMarkScaleArr addObject:@"24"];
}else{
[_xMarkScaleArr addObject:Y_Str];
}
}
//多少个X轴的分割线
int count = 6;
if(_xMarkScaleArr.count > count + 2){
_xMarkScaleArr = [self setArrWithArray:_xMarkScaleArr AndWantCount:count];
}
}
_xScaleMarkLEN = (self.frame.size.width - HORIZON_YMARKLAB_YAXISLINE - YMARKLAB_WIDTH - XMARKLAB_WIDTH / 2.0) / (_xMarkScaleArr.count - 1);
}
//抽离成想要的个数
-(NSMutableArray*)setArrWithArray:(NSMutableArray*)tempArr AndWantCount:(int)count{
NSInteger tempCount = (tempArr.count - 2) % (count-2);
for (int i = 1 ; i < tempArr.count-1; i++) {
if((i%(count-2)) != 0){
tempArr[i] = @"";
}
}
return tempArr;
}
/**
* 设置折线图显示的数据和对应X坐标轴刻度标签 (日期 eg: 2020-04-02 日期制)
*
* @param xMarkTitlesFDayAndValues 折线图显示的数据
* @param titleKey 标签(日期 eg: 2020-04-02
* @param valueKey 数据 (:80)
*/
- (void)setXMarkTitlesDayAndValues:(NSArray *)xMarkTitlesFDayAndValues titleKey:(NSString *)titleKey valueKey:(NSString *)valueKey{
//归整成 当天的 00:00
NSMutableArray *arr = [NSMutableArray array];
NSDateFormatter*dateFormatter = [[NSDateFormatter alloc]init];
[dateFormatter setDateFormat:@"yyyy-MM-dd"];
for (NSDictionary *item in xMarkTitlesFDayAndValues)
{
NSNumber *timeNumber = item[titleKey];
NSString *dayStr = [self getDateFormatWithStr:@"yyyy-MM-dd" date:[NSDate dateWithTimeIntervalSince1970:timeNumber.doubleValue]];
NSNumber *newTimeNumber = @([dateFormatter dateFromString:dayStr].timeIntervalSince1970);
NSMutableDictionary *tempDic = [NSMutableDictionary dictionaryWithDictionary:item];
[tempDic setValue:newTimeNumber forKey:titleKey];
[arr addObject:tempDic];
}
_xMarkTitlesAndValues = arr;
xMarkValues = [NSMutableArray array];
valueArray = [NSMutableArray array];
//其余没数据的天数补0
//是为 日期(7) 格式
_type = 1;
int howManyDays = 7;
_xMarkScaleArr = [NSMutableArray array];
for (int i = 0; i < howManyDays; i++)
{
NSString *dayStr;
NSString *dayStr1;
if(i == howManyDays-1){
//今天
dayStr = [self getDateFormatWithStr:@"yyyy-MM-dd" date:[NSDate date]];
// [_xMarkScaleArr addObject:[dayStr componentsSeparatedByString:@"-"].lastObject];
dayStr1 = [self getDateFormatWithStr:@"MM.dd" date:[NSDate date]];
[_xMarkScaleArr addObject:@"今天"];
xMarkMaxValue = [dateFormatter dateFromString:dayStr].timeIntervalSince1970;
}
else
{
//几天前
dayStr = [self getDateFormatWithStr:@"yyyy-MM-dd" date: [self ddpGetExpectTimestamp:0 month:0 day:i+1-howManyDays]];
//[_xMarkScaleArr addObject:[dayStr componentsSeparatedByString:@"-"].lastObject];
dayStr1 = [self getDateFormatWithStr:@"MM.dd" date: [self ddpGetExpectTimestamp:0 month:0 day:i+1-howManyDays]];
[_xMarkScaleArr addObject:dayStr1];
if(i==0){
xMarkMinValue = [dateFormatter dateFromString:dayStr].timeIntervalSince1970;
}
}
NSNumber *tempTimeStamp = @([dateFormatter dateFromString:dayStr].timeIntervalSince1970);
NSNumber *tempData = @(0);
for (NSDictionary *dic in _xMarkTitlesAndValues) {
if ([[dic objectForKey:titleKey] integerValue] == tempTimeStamp.integerValue){
tempData = [dic objectForKey:valueKey];
}
}
[xMarkValues addObject:tempTimeStamp];
[valueArray addObject:tempData];
}
NSLog(@"_xMarkScaleArr: %@",_xMarkScaleArr);
}
#pragma mark 绘图
- (void)mapping {
static CGFloat topToContainView = 0.f;
static CGFloat bottomToContainView = 0.f;
if (self.title) {
self.titleLab = [[UILabel alloc] initWithFrame:CGRectMake(0, 5, CGRectGetWidth(self.frame), 20)];
self.titleLab.text = self.title;
self.titleLab.textColor = [UIColor blackColor];
self.titleLab.font = [UIFont systemFontOfSize:15];
self.titleLab.textAlignment = NSTextAlignmentCenter;
[self addSubview:self.titleLab];
topToContainView = 25;
}
if(self.X_UnintTitle){
bottomToContainView = 21;
UILabel *unintLabel = [[UILabel alloc] initWithFrame:CGRectMake(0,self.frame.size.height - bottomToContainView, CGRectGetWidth(self.frame), bottomToContainView)];
unintLabel.text = _X_UnintTitle;
unintLabel.textColor = [UIColor colorWithRed:102.0f/255.0f green:102.0f/255.0f blue:102.0f/255.0f alpha:1.0f];
unintLabel.font = [UIFont systemFontOfSize:12];
unintLabel.textAlignment = NSTextAlignmentCenter;
[self addSubview:unintLabel];
}
if (!self.xMarkTitlesAndValues) {
xMarkValues = @[@1,@2,@3,@4,@5].mutableCopy;
valueArray = @[@2,@2,@2,@2,@2].mutableCopy;
}
if (!self.yMarkTitles) {
if(_Ycount == 0){
_Ycount = 6;
}
NSMutableArray *tempYMarkTitles = [NSMutableArray array];
CGFloat Yunint = (_maxValue - _minValue) / (_Ycount-1);
for (int i = 0; i < _Ycount; i++) {
[tempYMarkTitles addObject:[NSString stringWithFormat:@"%@",[NSNumber numberWithFloat:_minValue + (Yunint * i)]]];
}
// [tempYMarkTitles addObject:@""];
// LineGraphView.yMarkTitles = @[@"0",@"20",@"40",@"60",@"80",@"100"]; // Y轴刻度标签
_yMarkTitles = [tempYMarkTitles copy];
// if(tempYMarkTitles.count == 0){
// self.yMarkTitles = @[@0,@1,@2,@3,@4,@5];
// }
}
if (self.maxValue == 0) {
self.maxValue = 5;
}
self.scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, topToContainView, self.frame.size.width,self.frame.size.height - topToContainView-bottomToContainView)];
[self.scrollView setShowsHorizontalScrollIndicator:NO];
[self.scrollView setShowsVerticalScrollIndicator:NO];
[self addSubview:self.scrollView];
self.lineGraphContentView = [[FBYLineGraphContentView alloc] initWithFrame:self.scrollView.bounds];
self.lineGraphContentView.chartType = self.type;
self.lineGraphContentView.MainColor = self.MainColor;
self.lineGraphContentView.XYAxisColor = self.XYAxisColor;
self.lineGraphContentView.yMarkTitles = self.yMarkTitles;
self.lineGraphContentView.minValue = self.minValue;
self.lineGraphContentView.maxValue = self.maxValue;
self.lineGraphContentView.upperValue = self.upperValue;
self.lineGraphContentView.lowerValue = self.lowerValue;
self.lineGraphContentView.xMarkScaleArr = self.xMarkScaleArr;
self.lineGraphContentView.xMarkValues = xMarkValues;
self.lineGraphContentView.xMarkmaxValue = xMarkMaxValue;
self.lineGraphContentView.xMarkminValue = xMarkMinValue;
self.lineGraphContentView.xScaleMarkLEN = self.xScaleMarkLEN;
self.lineGraphContentView.valueArray = valueArray;
self.lineGraphContentView.maxValue = self.maxValue;
[self.scrollView addSubview:self.lineGraphContentView];
[self.lineGraphContentView mapping];
self.scrollView.contentSize = self.lineGraphContentView.bounds.size;
}
#pragma mark 更新数据
- (void)reloadDatas {
[self.lineGraphContentView reloadDatas];
}
- (NSTimeInterval)getDateFormatWithStr:(NSString *)dateFormat WithDateStr:(NSString*)dayStr{
NSDateFormatter*dateFormatter = [[NSDateFormatter alloc]init];
[dateFormatter setDateFormat:dateFormat];
return [dateFormatter dateFromString:dayStr].timeIntervalSince1970;
}
- (NSString *)getDateFormatWithStr:(NSString *)dateFormat timeInterval:(NSTimeInterval)timeInterval{
return [self getDateFormatWithStr:dateFormat date:[NSDate dateWithTimeIntervalSince1970:timeInterval]];
}
- (NSString *)getDateFormatWithStr:(NSString *)dateFormat date:(NSDate *)date{
NSDateFormatter*dateFormatter = [[NSDateFormatter alloc]init];
[dateFormatter setDateFormat:dateFormat];
return [dateFormatter stringFromDate:date];
}
///< 获取当前时间的: 前一周(day:-7)丶前一个月(month:-30)丶前一年(year:-1)的时间 NSDate
- (NSDate *)ddpGetExpectTimestamp:(NSInteger)year month:(NSUInteger)month day:(NSUInteger)day {
///< 当前时间
NSDate *currentdata = [NSDate date];
///< NSCalendar -- 日历类,它提供了大部分的日期计算接口,并且允许您在NSDate和NSDateComponents之间转换
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
/*
///< NSDateComponents:时间容器,一个包含了详细的年月日时分秒的容器。
///< 下例:获取指定日期的年,月,日
NSDateComponents *comps = nil;
comps = [calendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay fromDate:currentdata];
NSLog(@"年 year = %ld",comps.year);
NSLog(@"月 month = %ld",comps.month);
NSLog(@"日 day = %ld",comps.day);*/
NSDateComponents *datecomps = [[NSDateComponents alloc] init];
[datecomps setYear:year?:0];
[datecomps setMonth:month?:0];
[datecomps setDay:day?:0];
///< dateByAddingComponents: 在参数date基础上,增加一个NSDateComponents类型的时间增量
NSDate *calculatedate = [calendar dateByAddingComponents:datecomps toDate:currentdata options:0];
// ///< 打印推算时间
// NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
// [formatter setDateFormat:@"yyyy-MM-dd hh:mm:ss"];
// NSString *calculateStr = [formatter stringFromDate:calculatedate];
//
// xLog(@"calculateStr 推算时间: %@",calculateStr );
//
// ///< 预期的推算时间
// NSString *result = [NSString stringWithFormat:@"%ld", (long)[calculatedate timeIntervalSince1970]];
return calculatedate;
}
@end