ios ~ 如GIF图效果:曲线图 📈:CAShapeLayer
2022-08-10 本文已影响0人
阳光下的叶子呵
🧶先上图:
-动图.gif- | -静态图- |
---|---|
温度曲线图.GIF | 静态图.jpeg |
本人的获取触摸时,手指在iphone📱屏幕上的位置 1、ios ~ 手指在view或window上的位置 x、y
2、【Touch:iOS判断当前点击的位置是否在某个视图上】
判断是否触摸touch了某个View(contentView)
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event {
// 手指触摸 移动
// iOS 怎么样获取手指在view上滑动的起点坐标与终点坐标
UITouch *touch = touches.anyObject;
CGPoint point = [touch locationInView:self.progress_lineBackView];
NSLog(@"移动中: 手指所在 折线图📈上的位置 point.X ==== %f,\n point.Y ==== %f", point.x, point.y);
/**
* locationView 如果传入的是需要判断视图(self.blueView)的父视图,
* CGRectContainsPoint则需要传入需要判断视图(self.blueView)的frame,
* 否则不需要判断父视图,只需要判断当前子视图,需传入 子视图的bounds,即self.progress_lineBackView.bounds。
*/
// 如果矩形不为null,或空,并且该点位于矩形内,返回YES,在范围外面 返回NO
if (!CGRectContainsPoint(self.progress_lineBackView.frame, point)) {
NSLog(@"触摸 错错错❎");
} else {
NSLog(@"😆 触摸 对了");
}
}
因为,底层View是一个UITableView
,在左右滑动时(即point.x > point.y
时,暂时禁止
tableview滑动,触摸结束时,恢复
tableview的滑动)。
一、原理:
(1)先将紫色view
创建出来,(40日,一天一个数据,共40个),将紫色view
分成40个色块,每块空白的色块上面,在每个空白色块上面,各画出一个竖线
,在这里我设置了竖线
背景色的为半透明
,之后滑到那个色块的范围
时,改变其上的竖线
颜色;
(2)在淡蓝色
的view上创建一个CAShapeLayer
(即,折线图的父layer
),在上面可以画出折线图,并将那一个个端点
的坐标,保存到数组circleArray
,方便之后,画出小圆点
;
(3)最后就是上边的提示框
了,在某一个范围内移动(改变其X轴的偏移量
,和数据)。
以上,GIF图中,不同颜色的view
或layer
,一层层叠加覆盖,最后将那几个背景色设置为透明色
获取触摸屏幕时的坐标:
// iOS 怎么样获取手指在self.view上的坐标,
UITouch *touch = touches.anyObject;
CGPoint point = [touch locationInView:self.view];
系统的触摸
方法:(在这里,只是用了前三个
,即,开始
、移动
、结束
,三个步骤)
// 开始触摸
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
// 移动
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
// 结束触摸
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
// 取消触摸
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
二、代码:
// 40日降水 折线图(📈) :高度height = 8 + 247 + 32
#import "GWHomeDays_TableFooterView.h"
@interface GWHomeDays_TableFooterView ()
@property (nonatomic, strong) UIImageView *backImg;
@property (nonatomic, strong) UIView *backView;
@property (nonatomic, strong) UILabel *titleL;
@property (nonatomic, strong) UIImageView *topSeparateImg;
/**
* 升温 降温 降水 次数
*/
@property (nonatomic, strong) UILabel *statisticsTipsL;
/**
* 1、lineBackRangeView 折线图📈:width:256, height:104,
*
* 2、lineLayerRangeView 折线范围:从上面y=8的位置开始, width:256,height:78
* 40个块,竖线在每一个块的中间 width:256/40 = 6.4,下边对应雨图🌧、雪图❄️、灰色○(无降水)
* 3、fatherLayer 创建曲线图的父layer:78-8 ,给小圆圈⭕️预留位置,小圆圈 6x6
*/
@property (nonatomic, strong) UIView *lineBackRangeView; // 整个折线图范围view
@property (nonatomic, strong) UIView *lineLayerRangeView;// 划线范围范围view(有数据之后,再创建)
@property (nonatomic, strong) CAShapeLayer *fatherLayer; // 折线图的父layer (有数据之后,再创建)
@property (nonatomic, strong) CAShapeLayer *circleBackLayer; // 小圆点的背景layer,(手指touch移动的时候,删除 并重新绘制小圆点)
@property (nonatomic, strong) NSArray *circleArray; // 小圆点的坐标CGPoint数组
@property (nonatomic, strong) UIButton *weatherTipsBut; // 在触摸曲线图时,显示小圆点的同时,显示提示框,会随着左右滑动(改变偏移量)
// 折线图其他标签🏷:(降水量、时间、降水类型图标)
@property (nonatomic, strong) UILabel *highestTempL; // 最高温
@property (nonatomic, strong) UILabel *lowestTempL; // 最低温
@property (nonatomic, strong) UILabel *rainTitleL; // "降水"
@property (nonatomic, strong) UIView *rainBackImgView; // 下方的40个降水图标 (有数据之后,再创建)
/** 最下边的 时间 */
@property (nonatomic, strong) UILabel *rainTimeL1;
@property (nonatomic, strong) UILabel *rainTimeL2;
@property (nonatomic, strong) UILabel *rainTimeL3;
@property (nonatomic, strong) UILabel *rainTimeL4;
@property (nonatomic, strong) UILabel *rainTimeL5;
@end
@implementation GWHomeDays_TableFooterView
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = UIColor.clearColor;
[self setupUI];
}
return self;
}
- (void)refreshTableFooterView {
// heat、cool、rain、snow、
if (self.fortyDaysModel.fortyStatistics.count >= 3) {
NSInteger heatCount = 0;
NSInteger coolCount = 0;
NSInteger rainCount = 0;
for (FortyStatisticsModel *statisticsModel in self.fortyDaysModel.fortyStatistics) {
if ([statisticsModel.type isEqualToString:@"heat"]) {
heatCount = statisticsModel.count;
}
if ([statisticsModel.type isEqualToString:@"cool"]) {
coolCount = statisticsModel.count;
}
if ([statisticsModel.type isEqualToString:@"rain"]) {
rainCount = statisticsModel.count;
}
}
self.statisticsTipsL.text = [NSString stringWithFormat:@"升温%ld次 / 降温%ld次 / 降水%ld次", heatCount, coolCount, rainCount];
}
if (self.statisticsTipsL.text.length > 0) {
NSMutableAttributedString *contentStr = [[NSMutableAttributedString alloc]initWithString:self.statisticsTipsL.text];
[contentStr addAttribute:NSFontAttributeName
value:[UIFont systemFontOfSize:12]
range:[self.statisticsTipsL.text rangeOfString:self.statisticsTipsL.text]];
[contentStr addAttribute:NSForegroundColorAttributeName value:RGBA(238, 197, 116, 1) range:[self.statisticsTipsL.text rangeOfString:self.statisticsTipsL.text]];
for (int i = 0; i < self.statisticsTipsL.text.length; i++) {
NSRange range = {i, 1};// 范围是起点和长度,而非起点和终点
NSString * subStr = [self.statisticsTipsL.text substringWithRange:range];
if ([subStr isStringContainNumberWith:subStr]) { // 判断是否是数字,改变数字的大小和颜色
[contentStr addAttribute:NSFontAttributeName
value:[UIFont systemFontOfSize:[UIScreen mainScreen].bounds.size.width/375*20 weight:UIFontWeightBold]
range:range];
[contentStr addAttribute:NSForegroundColorAttributeName value:RGBA(193, 162, 122, 1) range:range];
}
}
self.statisticsTipsL.attributedText = contentStr;
}
/**
* 折线图📈
*/
[self.lineLayerRangeView removeFromSuperview];
[self.fatherLayer removeFromSuperlayer];
[self.circleBackLayer removeFromSuperlayer];
[self.rainBackImgView removeFromSuperview];
[self.weatherTipsBut removeFromSuperview];
self.highestTempL.text = [NSString stringWithFormat:@"%ld°", self.fortyDaysModel.fortyTemp.highest.value];
self.lowestTempL.text = [NSString stringWithFormat:@"%ld°", self.fortyDaysModel.fortyTemp.lowest.value];
self.circleArray = [NSArray array];
if (self.fortyDaysModel.fortyDetail.count >= 40) { // 有数据时
self.rainTimeL1.text = self.fortyDaysModel.fortyDetail[0].date;
self.rainTimeL2.text = self.fortyDaysModel.fortyDetail[9].date;
self.rainTimeL3.text = self.fortyDaysModel.fortyDetail[19].date;
self.rainTimeL4.text = self.fortyDaysModel.fortyDetail[29].date;
self.rainTimeL5.text = self.fortyDaysModel.fortyDetail[39].date;
// 下边的一整行 是否有降水图标:
_rainBackImgView = [[UIView alloc] initWithFrame:CGRectMake([UIScreen mainScreen].bounds.size.width/375*50, [UIScreen mainScreen].bounds.size.width/375*(99 + 104 + 2), [UIScreen mainScreen].bounds.size.width/375*256, [UIScreen mainScreen].bounds.size.width/375*10)];
_rainBackImgView.backgroundColor = RGBA(221, 255, 247, 1);
[self.backView addSubview:self.rainBackImgView];
for (int i = 0; i < self.fortyDaysModel.fortyDetail.count; i++) {
FortyDetailModel *detailModel = self.fortyDaysModel.fortyDetail[i];
/** 降水 */
UIImageView *rainImg = [[UIImageView alloc] initWithFrame:CGRectMake([UIScreen mainScreen].bounds.size.width/375*(0.4 + 6.4*i), [UIScreen mainScreen].bounds.size.width/375*2, [UIScreen mainScreen].bounds.size.width/375*6, [UIScreen mainScreen].bounds.size.width/375*6)];
if (detailModel.precipitation == 1) {
rainImg.image = [UIImage imageNamed:@"club_weather_大雨点_icon"];
} else {
rainImg.image = [UIImage imageNamed:@"ic_weather_sunny_icon"];
}
// rainImg.tag = 100+i;
[self.rainBackImgView addSubview:rainImg];
/** 背景竖线 */
if ([self.lineBackRangeView viewWithTag:1000+i]) { // 如果,之前已经创建,就是用之前的,没有在创建
UIView *lineView = [self.lineBackRangeView viewWithTag:1000+i];
lineView.backgroundColor = RGBA(193, 162, 122, 0.1);
lineView.tag = 1000+i; // 手指触摸之后,改区域范围内的竖线 “不半透明”,添加标签
} else {
UIView *lineView = [[UIView alloc] initWithFrame:CGRectMake([UIScreen mainScreen].bounds.size.width/375*((6.4-1)/2 + 6.4*i), 0, 1, [UIScreen mainScreen].bounds.size.width/375*104)];
lineView.backgroundColor = RGBA(193, 162, 122, 0.1);
lineView.tag = 1000+i; // 手指触摸之后,改区域范围内的竖线 “不半透明”,添加标签
[self.lineBackRangeView addSubview:lineView];
}
/**
* 折线图📈:width:256, height:104,
*
* 折线范围:从上面y=8的位置开始, width:256,height:78
* 40个块,竖线在每一个块的中间 width:256/40 = 6.4,下边对应雨图🌧、雪图❄️、灰色○(无降水)
*/
_lineLayerRangeView = [[UIView alloc] initWithFrame:CGRectMake(0, [UIScreen mainScreen].bounds.size.width/375*8, [UIScreen mainScreen].bounds.size.width/375*256, [UIScreen mainScreen].bounds.size.width/375*78)];
_lineLayerRangeView.backgroundColor = RGBA(214, 249, 255, 0.5);
[self.lineBackRangeView addSubview:self.lineLayerRangeView];
/** 1、创建曲线图的父layer:78-7 ,给小圆圈⭕️预留位置 */
_fatherLayer = [[CAShapeLayer alloc] init];
_fatherLayer.strokeColor = [UIColor clearColor].CGColor;
UIBezierPath *bezierPath = [UIBezierPath
bezierPathWithRoundedRect:CGRectMake(0, 0, self.lineLayerRangeView.frame.size.width, [UIScreen mainScreen].bounds.size.width/375*(78))
byRoundingCorners:UIRectCornerTopLeft | UIRectCornerTopRight
cornerRadii:CGSizeMake([UIScreen mainScreen].bounds.size.width/375*0, [UIScreen mainScreen].bounds.size.width/375*0)];
_fatherLayer.lineWidth = [UIScreen mainScreen].bounds.size.width/375*0.01;
// 颜色
_fatherLayer.strokeColor = [UIColor clearColor].CGColor;
// 背景填充色
// _fatherLayer.fillColor = [UIColor clearColor].CGColor;
_fatherLayer.fillColor = RGBA(233, 255, 244, 0.5).CGColor;
_fatherLayer.path = [bezierPath CGPath];
[self.lineLayerRangeView.layer addSublayer:self.fatherLayer];
/** 小圆点⭕️的父layer */
_circleBackLayer = [[CAShapeLayer alloc] init];
_circleBackLayer.strokeColor = [UIColor clearColor].CGColor;
UIBezierPath *circleBezierPath = [UIBezierPath
bezierPathWithRoundedRect:CGRectMake(0, 0, self.lineLayerRangeView.frame.size.width, [UIScreen mainScreen].bounds.size.width/375*(78))
byRoundingCorners:UIRectCornerTopLeft | UIRectCornerTopRight
cornerRadii:CGSizeMake([UIScreen mainScreen].bounds.size.width/375*0, [UIScreen mainScreen].bounds.size.width/375*0)];
_circleBackLayer.lineWidth = [UIScreen mainScreen].bounds.size.width/375*0.01;
// 颜色
_circleBackLayer.strokeColor = [UIColor clearColor].CGColor;
// 背景填充色
_circleBackLayer.fillColor = [UIColor clearColor].CGColor;
_circleBackLayer.path = [circleBezierPath CGPath];
[self.lineLayerRangeView.layer addSublayer:self.circleBackLayer];
/// 先找到40天的 最大温度和最低温度:(获取曲线图📈的范围)
NSInteger maxTemp = 0;
NSInteger minTemp = 0;
for (int i = 0; i < self.fortyDaysModel.fortyDetail.count; i++) {
FortyDetailModel *detailModel = self.fortyDaysModel.fortyDetail[i];
NSInteger i_temp = detailModel.dayTemp;
// NSLog(@"一天的温度😆😆 %.2f 😆😆", i_temp);
if (i == 0) {
maxTemp = i_temp;
minTemp = i_temp;
}
if (maxTemp > i_temp) {
maxTemp = maxTemp;
} else {
maxTemp = i_temp;
}
if (minTemp > i_temp) {
minTemp = i_temp;
} else {
minTemp = minTemp;
}
}
// 温度之差 的 温度范围:温度三种情况都是这个减法获取温度的范围
NSInteger maxTempRange = maxTemp - minTemp;
double maxTempRange_value = 0;
if (maxTempRange == 0) {
maxTempRange_value = 0;
} else {
maxTempRange_value = 70 / maxTempRange;
}
// 小圆点的中心点坐标 point数组:
NSMutableArray *circleArray = [NSMutableArray arrayWithCapacity:0];
/** 温度 path */
UIBezierPath *temp_Path = [UIBezierPath bezierPath];
// 设置path的 起始点 和 其他点
for (int k = 0; k < self.fortyDaysModel.fortyDetail.count; k++) {
FortyDetailModel *detailModel = self.fortyDaysModel.fortyDetail[k];
NSInteger nextTemp = detailModel.dayTemp;
// 两个相邻 端点 的位置间隔:67
if (k == 0) { // 第一个,特殊处理
// 第一个点,起始点。
[temp_Path moveToPoint:CGPointMake([UIScreen mainScreen].bounds.size.width/375*6.4/2, [UIScreen mainScreen].bounds.size.width/375*(74 - maxTempRange_value * (nextTemp - minTemp)))];
// 端点位置CGPoint
CGPoint circlePoint = CGPointMake([UIScreen mainScreen].bounds.size.width/375*(6.4/2), [UIScreen mainScreen].bounds.size.width/375*(74 - maxTempRange_value * (nextTemp - minTemp)));
NSValue* circleValue = [NSValue valueWithCGPoint:circlePoint];
[circleArray addObject:circleValue];
} else {
FortyDetailModel *detailModel0 = self.fortyDaysModel.fortyDetail[k-1];
NSInteger lastTemp = detailModel0.dayTemp;
// 原理(这里的天气温度曲线):endPoint:下一个端点的x、y轴坐标, controlPoint1:X轴坐标是上一个startPoint.x和下一个endPoint.x的中间X坐标位置,Y轴是和上一个startPoint.y, controlPoint2:X轴坐标是上一个startPoint.x和下一个endPoint.x的中间X坐标位置,Y轴是和下一个endPoint.y,
[temp_Path addCurveToPoint:CGPointMake([UIScreen mainScreen].bounds.size.width/375*(6.4/2 + 6.4*k) , [UIScreen mainScreen].bounds.size.width/375*(74 - maxTempRange_value * (nextTemp - minTemp)))
controlPoint1:CGPointMake([UIScreen mainScreen].bounds.size.width/375*(6.4/2 + 6.4*k - 6.4/2), [UIScreen mainScreen].bounds.size.width/375*(74 - maxTempRange_value * (lastTemp - minTemp)))
controlPoint2:CGPointMake([UIScreen mainScreen].bounds.size.width/375*(6.4/2 + 6.4*k - 6.4/2), [UIScreen mainScreen].bounds.size.width/375*(74 - maxTempRange_value * ((nextTemp - minTemp))))];
// 端点位置CGPoint
CGPoint circlePoint = CGPointMake([UIScreen mainScreen].bounds.size.width/375*(6.4/2+ (6.4*k)), [UIScreen mainScreen].bounds.size.width/375*(74 - maxTempRange_value * (nextTemp - minTemp)));
NSValue* circleValue = [NSValue valueWithCGPoint:circlePoint];
[circleArray addObject:circleValue];
}
}
CAShapeLayer *temp_Layer = [[CAShapeLayer alloc] init];
// 线宽
temp_Layer.lineWidth = [UIScreen mainScreen].bounds.size.width/375*2;
// 线条的颜色
temp_Layer.strokeColor = RGBA(193, 162, 122, 1).CGColor;
// 背景填充色
temp_Layer.fillColor = [UIColor clearColor].CGColor;
// 将UIBezierPath类转换成CGPath,类似于UIColor的CGColor
temp_Layer.path = [temp_Path CGPath];
[self.fatherLayer addSublayer:temp_Layer];
/** 所有的曲线端点(小圆点)的位置:CGPoint */
self.circleArray = [circleArray copy];
/** 折线图📈中的小圆点 */
// for (NSValue *circleValue in circleArray) {
// /** 折线图📈中的小圆点 */
// CGPoint circlePoint = [circleValue CGPointValue];
//
// CAShapeLayer *circleLayer = [CAShapeLayer layer];
// // 线宽
// circleLayer.lineWidth = [UIScreen mainScreen].bounds.size.width/375*1; // 10 - 6 = 4
// circleLayer.lineCap = kCALineCapRound; // 端点样式
// circleLayer.lineJoin = kCALineJoinMiter; // 终点处理
// // 线条的颜色
// circleLayer.strokeColor = RGBA(193, 162, 122, 1).CGColor;
// // 背景填充色
// circleLayer.fillColor = [UIColor whiteColor].CGColor;
// // 设置线宽、线间距(虚线)
// // [circleLayer setLineDashPattern:[NSArray arrayWithObjects:[NSNumber numberWithInt:2], [NSNumber numberWithInt:2], nil]];
//
// // 设置半径
// CGFloat circleRadius = [UIScreen mainScreen].bounds.size.width/375*3;
// // bezierPathWithArcCenter 中心点,下面就让addSublayer了,那么就设置self.bezierBackImg.layer的 中心点就好了,宽/2,高/2
// UIBezierPath *circlePath = [UIBezierPath bezierPathWithArcCenter:circlePoint radius:circleRadius startAngle:(0*M_PI) endAngle:(2*M_PI) clockwise:YES]; // 终止点(60%几率时):(2*0.6 - 0.25)*M_PI,clockwise 顺时针 YES, 逆时针 NO
// circleLayer.path = [circlePath CGPath];
// [self.circleBackLayer addSublayer:circleLayer];
// }
// @property (nonatomic, strong) UIView *lineBackRangeView; // 整个折线图范围view
// @property (nonatomic, strong) UIView *lineLayerRangeView;// 划线范围范围view(有数据之后,再创建)
// @property (nonatomic, strong) CAShapeLayer *fatherLayer; // 折线图的父layer (有数据之后,再创建)
// UIView *lineView0 = [self.lineBackRangeView viewWithTag:1020];
// lineView0.backgroundColor = RGBA(193, 162, 122, 1);
}
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event {
// 手指触摸 开始
UITouch *touch = touches.anyObject;
CGPoint point = [touch locationInView:self.lineLayerRangeView];
NSLog(@"☺️☺️☺️ 开始触摸屏幕 手指所在 折线图📈上的位置 point.X ==== %f,\n point.Y ==== %f", point.x, point.y);
// 条件:有数据,并且 点击的是这个曲线的范围UIview内:
if (self.fortyDaysModel.fortyDetail.count >= 40 && CGRectContainsPoint(self.lineLayerRangeView.frame, point)) { // 有数据时
// (1) 竖线:
for (int i = 0; i < self.fortyDaysModel.fortyDetail.count; i++) {
CGFloat touch_Point_X = point.x;
NSInteger indexItem = touch_Point_X / ([UIScreen mainScreen].bounds.size.width/375*6.4);
UIView *lineView = [self.lineBackRangeView viewWithTag:1000+i]; // 找到加tag的那一个,该变其颜色
if (indexItem < self.circleArray.count && indexItem >= 0) {
lineView.backgroundColor = RGBA(193, 162, 122, 0.2);
}
if (i == indexItem) { // 手指在到第几个
lineView.backgroundColor = RGBA(193, 162, 122, 1);
}
}
// (2) 小圆点:
if (self.circleArray.count > 0) {
/** 折线图📈中的小圆点 */
CGFloat touch_Point_X = point.x;
NSInteger indexItem = touch_Point_X / ([UIScreen mainScreen].bounds.size.width/375*6.4);
if (indexItem < self.circleArray.count && indexItem >= 0) {
[self.circleBackLayer removeAllSublayers];// 删除所有子layer,重新绘制子layer
NSValue *circleValue = self.circleArray[indexItem];
CGPoint circlePoint = [circleValue CGPointValue];
CAShapeLayer *circleLayer = [CAShapeLayer layer];
// 线宽
circleLayer.lineWidth = [UIScreen mainScreen].bounds.size.width/375*1; // 10 - 6 = 4
circleLayer.lineCap = kCALineCapRound; // 端点样式
circleLayer.lineJoin = kCALineJoinMiter; // 终点处理
// 线条的颜色
circleLayer.strokeColor = RGBA(193, 162, 122, 1).CGColor;
// 背景填充色
circleLayer.fillColor = [UIColor whiteColor].CGColor;
// 设置线宽、线间距(虚线)
// [circleLayer setLineDashPattern:[NSArray arrayWithObjects:[NSNumber numberWithInt:2], [NSNumber numberWithInt:2], nil]];
// 设置半径
CGFloat circleRadius = [UIScreen mainScreen].bounds.size.width/375*3;
// bezierPathWithArcCenter 中心点,下面就让addSublayer了,那么就设置self.bezierBackImg.layer的 中心点就好了,宽/2,高/2
UIBezierPath *circlePath = [UIBezierPath bezierPathWithArcCenter:circlePoint radius:circleRadius startAngle:(0*M_PI) endAngle:(2*M_PI) clockwise:YES]; // 终止点(60%几率时):(2*0.6 - 0.25)*M_PI,clockwise 顺时针 YES, 逆时针 NO
circleLayer.path = [circlePath CGPath];
[self.circleBackLayer addSublayer:circleLayer];
}
}
// (3) 提示框:可移动 256 - 130 = 126 的距离,
// 计算到达 第几个四块内
CGFloat touch_Point_X = point.x;
NSInteger indexItem = touch_Point_X / ([UIScreen mainScreen].bounds.size.width/375*6.4);
if (indexItem < self.circleArray.count && indexItem >= 0) { // 防止数组越界
[self.weatherTipsBut removeFromSuperview];
FortyDetailModel *detailModel = self.fortyDaysModel.fortyDetail[indexItem];
_weatherTipsBut = [[UIButton alloc] initWithFrame:CGRectMake([UIScreen mainScreen].bounds.size.width/375*50, [UIScreen mainScreen].bounds.size.width/375*(83), [UIScreen mainScreen].bounds.size.width/375*130, [UIScreen mainScreen].bounds.size.width/375*23)];
[_weatherTipsBut setBackgroundImage:[UIImage imageNamed:@"addCity_bg_icon"] forState:UIControlStateNormal];
[_weatherTipsBut setTitleColor:UIColor.whiteColor forState:UIControlStateNormal];
_weatherTipsBut.titleLabel.font = [UIFont systemFontOfSize:[UIScreen mainScreen].bounds.size.width/375*12 weight:UIFontWeightRegular];
_weatherTipsBut.titleLabel.adjustsFontSizeToFitWidth = YES;
_weatherTipsBut.titleLabel.minimumScaleFactor = 0.3;
[self.backView addSubview:self.weatherTipsBut];
[self.weatherTipsBut setTitle:[NSString stringWithFormat:@"%@°", detailModel.detail] forState:UIControlStateNormal];
// 重新设置X轴的坐标偏移量:
if ((50 + indexItem*6.4) < (50 + 130/2)) { // 贴着 最左边
self.weatherTipsBut.frame = CGRectMake([UIScreen mainScreen].bounds.size.width/375*(50), [UIScreen mainScreen].bounds.size.width/375*(83), [UIScreen mainScreen].bounds.size.width/375*130, [UIScreen mainScreen].bounds.size.width/375*23);
} else if ((50 + indexItem*6.4) > (50 + 256 - 130/2)) { // 贴着 最右边
self.weatherTipsBut.frame = CGRectMake([UIScreen mainScreen].bounds.size.width/375*(50 + 256 - 130), [UIScreen mainScreen].bounds.size.width/375*(83), [UIScreen mainScreen].bounds.size.width/375*130, [UIScreen mainScreen].bounds.size.width/375*23);
} else { // (50 + indexItem*6.4) >= (50 + 130/2) && (50 + indexItem*6.4) <= (50 + 256 - 130/2)
self.weatherTipsBut.frame = CGRectMake([UIScreen mainScreen].bounds.size.width/375*(50 + indexItem*6.4 - 130/2), [UIScreen mainScreen].bounds.size.width/375*(83), [UIScreen mainScreen].bounds.size.width/375*130, [UIScreen mainScreen].bounds.size.width/375*23);
}
}
}
}
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event {
// 手指触摸 移动
// iOS 怎么样获取手指在view上滑动的起点坐标与终点坐标
UITouch *touch = touches.anyObject;
CGPoint point = [touch locationInView:self.lineLayerRangeView];
// NSLog(@"😆😆 手指所在 折线图📈上的位置 point.X ==== %f,\n point.Y ==== %f", point.x, point.y);
/**
* 当底层视图是个 UITableView,在触摸折线图📈,并在其📈上面左右滑动时,暂时禁止tableview的滑动效果,
*
* 再,触摸结束或point.y > point.x时,回复tableview的滑动
*
*/
if ([self.superview isKindOfClass:[UITableView class]]) {
UITableView *tableView = (UITableView *)self.superview;
if (point.x > point.y) {
tableView.scrollEnabled = NO;
} else {
tableView.scrollEnabled = YES;
}
}
// 条件:有数据,并且 点击的是这个曲线的范围UIview内:
if (self.fortyDaysModel.fortyDetail.count >= 40 && CGRectContainsPoint(self.lineLayerRangeView.frame, point)) { // 有数据时
// (1) 竖线:
for (int i = 0; i < self.fortyDaysModel.fortyDetail.count; i++) {
// 计算到达 第几个四块内
CGFloat touch_Point_X = point.x;
NSInteger indexItem = touch_Point_X / ([UIScreen mainScreen].bounds.size.width/375*6.4);
UIView *lineView = [self.lineBackRangeView viewWithTag:1000+i]; // 找到加tag的那一个,该变其颜色
if (indexItem < self.circleArray.count && indexItem >= 0) {
lineView.backgroundColor = RGBA(193, 162, 122, 0.2);
}
if (i == indexItem) { // 手指在到第几个
lineView.backgroundColor = RGBA(193, 162, 122, 1);
}
}
// (2) 小圆点:
if (self.circleArray.count > 0) {
/** 折线图📈中的小圆点 */
// 计算到达 第几个四块内
CGFloat touch_Point_X = point.x;
NSInteger indexItem = touch_Point_X / ([UIScreen mainScreen].bounds.size.width/375*6.4);
if (indexItem < self.circleArray.count && indexItem >= 0) {
[self.circleBackLayer removeAllSublayers];// 删除所有子layer,重新绘制子layer
NSValue *circleValue = self.circleArray[indexItem];
CGPoint circlePoint = [circleValue CGPointValue];
CAShapeLayer *circleLayer = [CAShapeLayer layer];
// 线宽
circleLayer.lineWidth = [UIScreen mainScreen].bounds.size.width/375*1; // 10 - 6 = 4
circleLayer.lineCap = kCALineCapRound; // 端点样式
circleLayer.lineJoin = kCALineJoinMiter; // 终点处理
// 线条的颜色
circleLayer.strokeColor = RGBA(193, 162, 122, 1).CGColor;
// 背景填充色
circleLayer.fillColor = [UIColor whiteColor].CGColor;
// 设置线宽、线间距(虚线)
// [circleLayer setLineDashPattern:[NSArray arrayWithObjects:[NSNumber numberWithInt:2], [NSNumber numberWithInt:2], nil]];
// 设置半径
CGFloat circleRadius = [UIScreen mainScreen].bounds.size.width/375*3;
// bezierPathWithArcCenter 中心点,下面就让addSublayer了,那么就设置self.bezierBackImg.layer的 中心点就好了,宽/2,高/2
UIBezierPath *circlePath = [UIBezierPath bezierPathWithArcCenter:circlePoint radius:circleRadius startAngle:(0*M_PI) endAngle:(2*M_PI) clockwise:YES]; // 终止点(60%几率时):(2*0.6 - 0.25)*M_PI,clockwise 顺时针 YES, 逆时针 NO
circleLayer.path = [circlePath CGPath];
[self.circleBackLayer addSublayer:circleLayer];
}
}
// (3) 提示框:可移动 256 - 130 = 126 的距离,
// 计算到达 第几个四块内
CGFloat touch_Point_X = point.x;
NSInteger indexItem = touch_Point_X / ([UIScreen mainScreen].bounds.size.width/375*6.4);
if (indexItem < self.circleArray.count && indexItem >= 0) { // 防止数组越界
[self.weatherTipsBut removeFromSuperview];
FortyDetailModel *detailModel = self.fortyDaysModel.fortyDetail[indexItem];
_weatherTipsBut = [[UIButton alloc] initWithFrame:CGRectMake([UIScreen mainScreen].bounds.size.width/375*50, [UIScreen mainScreen].bounds.size.width/375*(83), [UIScreen mainScreen].bounds.size.width/375*130, [UIScreen mainScreen].bounds.size.width/375*23)];
[_weatherTipsBut setBackgroundImage:[UIImage imageNamed:@"addCity_bg_icon"] forState:UIControlStateNormal];
[_weatherTipsBut setTitleColor:UIColor.whiteColor forState:UIControlStateNormal];
_weatherTipsBut.titleLabel.font = [UIFont systemFontOfSize:[UIScreen mainScreen].bounds.size.width/375*12 weight:UIFontWeightRegular];
_weatherTipsBut.titleLabel.adjustsFontSizeToFitWidth = YES;
_weatherTipsBut.titleLabel.minimumScaleFactor = 0.3;
[self.backView addSubview:self.weatherTipsBut];
[self.weatherTipsBut setTitle:[NSString stringWithFormat:@"%@°", detailModel.detail] forState:UIControlStateNormal];
// 重新设置X轴的坐标偏移量:
if ((50 + indexItem*6.4) < (50 + 130/2)) { // 贴着 最左边
self.weatherTipsBut.frame = CGRectMake([UIScreen mainScreen].bounds.size.width/375*(50), [UIScreen mainScreen].bounds.size.width/375*(83), [UIScreen mainScreen].bounds.size.width/375*130, [UIScreen mainScreen].bounds.size.width/375*23);
} else if ((50 + indexItem*6.4) > (50 + 256 - 130/2)) { // 贴着 最右边
self.weatherTipsBut.frame = CGRectMake([UIScreen mainScreen].bounds.size.width/375*(50 + 256 - 130), [UIScreen mainScreen].bounds.size.width/375*(83), [UIScreen mainScreen].bounds.size.width/375*130, [UIScreen mainScreen].bounds.size.width/375*23);
} else { // (50 + indexItem*6.4) >= (50 + 130/2) && (50 + indexItem*6.4) <= (50 + 256 - 130/2)
self.weatherTipsBut.frame = CGRectMake([UIScreen mainScreen].bounds.size.width/375*(50 + indexItem*6.4 - 130/2), [UIScreen mainScreen].bounds.size.width/375*(83), [UIScreen mainScreen].bounds.size.width/375*130, [UIScreen mainScreen].bounds.size.width/375*23);
}
}
}
}
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event {
// 手指触摸 结束
NSLog(@"☺️☺️☺️☺️ 手指触摸 结束");
/**
* 当底层视图是个 UITableView,在触摸折线图📈,并在其📈上面左右滑动时,暂时禁止tableview的滑动效果,
*
* 再,触摸结束或point.y > point.x时,回复tableview的滑动
*
*/
if ([self.superview isKindOfClass:[UITableView class]]) {
UITableView *tableView = (UITableView *)self.superview;
tableView.scrollEnabled = YES;
}
}
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event {
// 取消触摸时
NSLog(@"☺️☺️ 取消触摸 ☺️☺️☺️☺️");
}
UI添加到该self.View 上:
- (void)setupUI {
_backImg = [[UIImageView alloc] initWithFrame:CGRectMake([UIScreen mainScreen].bounds.size.width/375*24, [UIScreen mainScreen].bounds.size.width/375*8, [UIScreen mainScreen].bounds.size.width/375*(375 - 24*2), [UIScreen mainScreen].bounds.size.width/375*247)];
_backImg.image = [UIImage imageNamed:@"cityList_bg_icon"];
[self addSubview:self.backImg];
_backView = [[UIView alloc] initWithFrame:CGRectMake([UIScreen mainScreen].bounds.size.width/375*24, [UIScreen mainScreen].bounds.size.width/375*8, [UIScreen mainScreen].bounds.size.width/375*(375 - 24*2), [UIScreen mainScreen].bounds.size.width/375*247)];
_backView.backgroundColor = UIColor.clearColor;
[self addSubview:self.backView];
_titleL = [[UILabel alloc] init];
_titleL.text = @"40日趋势";
_titleL.textColor = RGBA(9, 9, 9, 1);
_titleL.textAlignment = NSTextAlignmentLeft;
_titleL.font = [UIFont systemFontOfSize:[UIScreen mainScreen].bounds.size.width/375*14 weight:UIFontWeightMedium];
[self.backView addSubview:self.titleL];
[self.titleL makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(self.backView.mas_top).offset([UIScreen mainScreen].bounds.size.width/375*18);
make.left.mas_equalTo(self.backView.mas_left).offset([UIScreen mainScreen].bounds.size.width/375*18);
}];
_topSeparateImg = [[UIImageView alloc] init];
_topSeparateImg.backgroundColor = RGBA(193, 162, 122, 0.7);
[self.backView addSubview:self.topSeparateImg];
[self.topSeparateImg makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(self.titleL.mas_bottom).offset([UIScreen mainScreen].bounds.size.width/375*3);
make.left.mas_equalTo(self.backView.mas_left).offset([UIScreen mainScreen].bounds.size.width/375*15);
make.right.mas_equalTo(self.backView.mas_right).offset(-[UIScreen mainScreen].bounds.size.width/375*15);
make.height.mas_equalTo([UIScreen mainScreen].bounds.size.width/375*14);
}];
_statisticsTipsL = [[UILabel alloc] init];
_statisticsTipsL.textColor = RGBA(9, 9, 9, 0.7);
_statisticsTipsL.textAlignment = NSTextAlignmentCenter;
_statisticsTipsL.font = [UIFont systemFontOfSize:[UIScreen mainScreen].bounds.size.width/375*12 weight:UIFontWeightRegular];
[self.backView addSubview:self.statisticsTipsL];
[self.statisticsTipsL makeConstraints:^(MASConstraintMaker *make) {
make.bottom.mas_equalTo(self.backView.mas_top).offset([UIScreen mainScreen].bounds.size.width/375*(65+14));
make.left.mas_equalTo(self.backView.mas_left).offset([UIScreen mainScreen].bounds.size.width/375*18);
make.right.mas_equalTo(self.backView.mas_right).offset(-[UIScreen mainScreen].bounds.size.width/375*18);
}];
/**
* 折线图📈:width:256, height:104,
*
* 折线范围:从上面y=8的位置开始, width:256,height:78
* 40个块,竖线在每一个块的中间,下边对应雨图🌧、雪图❄️、灰色○(无降水)
*/
_lineBackRangeView = [[UIView alloc] initWithFrame:CGRectMake([UIScreen mainScreen].bounds.size.width/375*50, [UIScreen mainScreen].bounds.size.width/375*99, [UIScreen mainScreen].bounds.size.width/375*256, [UIScreen mainScreen].bounds.size.width/375*104)];
self.lineBackRangeView.backgroundColor = RGBA(232, 226, 255, 1);
[self.backView addSubview:self.lineBackRangeView];
// 折线图其他标签🏷:(降水量、时间、降水类型图标)
// 最高温
_highestTempL = [[UILabel alloc] init];
_highestTempL.text = @"°";
_highestTempL.textColor = RGBA(0, 0, 0, 0.5);
_highestTempL.textAlignment = NSTextAlignmentLeft;
_highestTempL.font = [UIFont systemFontOfSize:[UIScreen mainScreen].bounds.size.width/375*12 weight:UIFontWeightRegular];
[self.backView addSubview:self.highestTempL];
[self.highestTempL makeConstraints:^(MASConstraintMaker *make) {
make.centerY.mas_equalTo(self.lineBackRangeView.mas_top).offset([UIScreen mainScreen].bounds.size.width/375*8);
make.left.mas_equalTo(self.backView.mas_left).offset([UIScreen mainScreen].bounds.size.width/375*18);
}];
// 最低温
_lowestTempL = [[UILabel alloc] init];
_lowestTempL.text = @"°";
_lowestTempL.textColor = RGBA(0, 0, 0, 0.5);
_lowestTempL.textAlignment = NSTextAlignmentLeft;
_lowestTempL.font = [UIFont systemFontOfSize:[UIScreen mainScreen].bounds.size.width/375*12 weight:UIFontWeightRegular];
[self.backView addSubview:self.lowestTempL];
[self.lowestTempL makeConstraints:^(MASConstraintMaker *make) {
make.centerY.mas_equalTo(self.lineBackRangeView.mas_top).offset([UIScreen mainScreen].bounds.size.width/375*(8+78));
make.left.mas_equalTo(self.backView.mas_left).offset([UIScreen mainScreen].bounds.size.width/375*18);
}];
_rainTitleL = [[UILabel alloc] init];
_rainTitleL.text = @"降水";
_rainTitleL.textColor = RGBA(0, 0, 0, 0.5);
_rainTitleL.textAlignment = NSTextAlignmentLeft;
_rainTitleL.font = [UIFont systemFontOfSize:[UIScreen mainScreen].bounds.size.width/375*10 weight:UIFontWeightRegular];
[self.backView addSubview:self.rainTitleL];
[self.rainTitleL makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(self.lineBackRangeView.mas_bottom).offset([UIScreen mainScreen].bounds.size.width/375*2);
make.left.mas_equalTo(self.backView.mas_left).offset([UIScreen mainScreen].bounds.size.width/375*18);
}];
/** 最下边的 时间 */
_rainTimeL1 = [[UILabel alloc] init];
_rainTimeL1.textColor = RGBA(0, 0, 0, 0.5);
_rainTimeL1.textAlignment = NSTextAlignmentLeft;
_rainTimeL1.font = [UIFont systemFontOfSize:[UIScreen mainScreen].bounds.size.width/375*10 weight:UIFontWeightRegular];
[self.backView addSubview:self.rainTimeL1];
[self.rainTimeL1 makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(self.rainTitleL.mas_bottom).offset([UIScreen mainScreen].bounds.size.width/375*3);
make.left.mas_equalTo(self.backView.mas_left).offset([UIScreen mainScreen].bounds.size.width/375*50);
make.width.mas_equalTo([UIScreen mainScreen].bounds.size.width/375*30);
}];
_rainTimeL2 = [[UILabel alloc] init];
_rainTimeL2.textColor = RGBA(0, 0, 0, 0.5);
_rainTimeL2.textAlignment = NSTextAlignmentCenter;
_rainTimeL2.font = [UIFont systemFontOfSize:[UIScreen mainScreen].bounds.size.width/375*10 weight:UIFontWeightRegular];
[self.backView addSubview:self.rainTimeL2];
[self.rainTimeL2 makeConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(self.rainTimeL1);
make.left.mas_equalTo(self.backView.mas_left).offset([UIScreen mainScreen].bounds.size.width/375*(50 +30 +26.5));
make.width.mas_equalTo([UIScreen mainScreen].bounds.size.width/375*30);
}];
_rainTimeL3 = [[UILabel alloc] init];
_rainTimeL3.textColor = RGBA(0, 0, 0, 0.5);
_rainTimeL3.textAlignment = NSTextAlignmentCenter;
_rainTimeL3.font = [UIFont systemFontOfSize:[UIScreen mainScreen].bounds.size.width/375*10 weight:UIFontWeightRegular];
[self.backView addSubview:self.rainTimeL3];
[self.rainTimeL3 makeConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(self.rainTimeL1);
make.left.mas_equalTo(self.backView.mas_left).offset([UIScreen mainScreen].bounds.size.width/375*(50 +30*2 +26.5*2));
make.width.mas_equalTo([UIScreen mainScreen].bounds.size.width/375*30);
}];
_rainTimeL4 = [[UILabel alloc] init];
_rainTimeL4.textColor = RGBA(0, 0, 0, 0.5);
_rainTimeL4.textAlignment = NSTextAlignmentCenter;
_rainTimeL4.font = [UIFont systemFontOfSize:[UIScreen mainScreen].bounds.size.width/375*10 weight:UIFontWeightRegular];
[self.backView addSubview:self.rainTimeL4];
[self.rainTimeL4 makeConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(self.rainTimeL1);
make.left.mas_equalTo(self.backView.mas_left).offset([UIScreen mainScreen].bounds.size.width/375*(50 +30*3 +26.5*3));
make.width.mas_equalTo([UIScreen mainScreen].bounds.size.width/375*30);
}];
_rainTimeL5 = [[UILabel alloc] init];
_rainTimeL5.textColor = RGBA(0, 0, 0, 0.5);
_rainTimeL5.textAlignment = NSTextAlignmentRight;
_rainTimeL5.font = [UIFont systemFontOfSize:[UIScreen mainScreen].bounds.size.width/375*10 weight:UIFontWeightRegular];
[self.backView addSubview:self.rainTimeL5];
[self.rainTimeL5 makeConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(self.rainTimeL1);
make.left.mas_equalTo(self.backView.mas_left).offset([UIScreen mainScreen].bounds.size.width/375*(50 +30*4 +26.5*4));
make.width.mas_equalTo([UIScreen mainScreen].bounds.size.width/375*30);
}];
}