iOS开发点滴【最常用的几个文章:字体颜色、换行等等】iOS~ 贝塞尔曲线:UIBezierPath和CAShapeLayer

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图中,不同颜色的viewlayer,一层层叠加覆盖,最后将那几个背景色设置为透明色

获取触摸屏幕时的坐标:
// 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);
    }];
    
}

上一篇下一篇

猜你喜欢

热点阅读