iOS-雷達數據圖

這次簡單的寫一個關於雷達數據顯示的案列,實現簡單,主要用UIBezierPath以及CAShapeLayer,如下圖:
在這裏插入圖片描述
具體實現:

思路:如何繪製上圖中的六邊形?我們只需要確定最外層六邊形的邊長(確定大小)和六邊形的中心點的座標(確定位置)。繪製幾層六邊形可以隨意控制(把邊平分幾段)。然後添加每個頂點旁邊的文本。最後根據相應的數據值繪製layer(顏色層)。

實現代碼:

1、初始化默認數據:

- (void)configureDefaultData
{
    _lineWidth = 0.5;
    _valueWidth = 0.8;
    _maxValue = 100.0;///<最大值,默認100(對應產品需求數據裏面的最大值)
    
    _perAngle = 2 * M_PI / SideCount;

    //默認六邊形在view的中間位置(任意調整)
    _centerPoint = CGPointMake(self.yy_width * 0.5, self.yy_height * 0.5);
    //根據中間點和邊長就可以確定六邊形最外層六個點座標;如果知道層數那麼就可以確定每一層六邊形的每個點的座標
    _sideLength = 100.0;//默認六邊形邊長爲 100
    _layerCount = 5;//默認層數爲 5 層
    
    _layerStrokeColor = [[UIColor blueColor] colorWithAlphaComponent:0.2];
    
    _layerFillColor = [UIColor clearColor];
    
}

2、繪製六邊形:

    //分割 num 後每格長度
    CGFloat dLength = _sideLength / _layerCount;

//    CGFloat sinV = sin(Degrees_To_Radians(30));//sin30度值
//    CGFloat cosV = cos(Degrees_To_Radians(30));//cos30度值
    
    // 繪製 num 個六邊形
    for (NSInteger i = 0; i < _layerCount; i ++) {
        
        //每一層邊長
        CGFloat perSideLength = _sideLength - (dLength * i);
        
        //每一層貝塞爾曲線
        UIBezierPath *bezierPath = [UIBezierPath bezierPath];
        
        //確定六個頂點座標
        for (NSInteger j = 0; j < SideCount; j++) {
            CGPoint tempPoint = CGPointMake(_centerPoint.x - perSideLength * cos(_perAngle * (j+1)) ,_centerPoint.y - perSideLength * sin(_perAngle * (j+1)));
            if (j == 0) {
                [bezierPath moveToPoint:tempPoint];
            } else {
                [bezierPath addLineToPoint:tempPoint];
            }
            //保存最外層六邊形的六個頂點座標
            if (i == 0) {
                [self.outerLayerPoints addObject:[NSValue valueWithCGPoint:tempPoint]];
            }
        }
        
        [bezierPath closePath];
        
        CAShapeLayer *aLayer = [CAShapeLayer layer];
        aLayer.lineWidth = _lineWidth;
        aLayer.fillColor = _layerFillColor.CGColor;
        aLayer.strokeColor = _layerStrokeColor.CGColor;
        aLayer.lineCap = kCALineCapSquare;
        aLayer.path = bezierPath.CGPath;
        [self.layer addSublayer:aLayer];
    }
    
    //各個定點向中心點連線
    for (NSInteger i = 0; i < self.outerLayerPoints.count; i++) {
        CGPoint point = [self.outerLayerPoints[i] CGPointValue];
        UIBezierPath *bezierPath = [UIBezierPath bezierPath];
        [bezierPath moveToPoint:point];
        [bezierPath addLineToPoint:_centerPoint];
        
        CAShapeLayer *aLayer = [CAShapeLayer layer];
        aLayer.lineWidth = _lineWidth;
        aLayer.fillColor = _layerFillColor.CGColor;
        aLayer.strokeColor = _layerStrokeColor.CGColor;
        aLayer.lineCap = kCALineCapSquare;
        aLayer.path = bezierPath.CGPath;
        [self.layer addSublayer:aLayer];
    }

3、繪製數據值層:

- (void)initValuePoint
{
    //移除之前的值layer
    for (CAShapeLayer *layer in self.layerArray) {
        [layer removeFromSuperlayer];
    }
    [self.layerArray removeAllObjects];

    for (NSInteger i = 0; i < _values.count; i ++) {
        
        NSArray *templeValues = _values[i];
        //繪製
        UIBezierPath *path = [UIBezierPath bezierPath];
        
        for (NSInteger j = 0; j < templeValues.count; j++) {
            
            CGFloat value = [[templeValues objectAtIndex:j] floatValue];
            
            //注意:這裏最大值以及邊長默認設置都是100
            //value = value > _sideLength ? _sideLength : value;
            //換算
            value = (value / _maxValue) * _sideLength > _sideLength ? _sideLength : (value / _maxValue) * _sideLength;
            
            CGPoint tempPoint = CGPointMake(_centerPoint.x - value * cos(_perAngle * (j+1)) ,_centerPoint.y - value * sin(_perAngle * (j+1)));
            if (j == 0) {
                [path moveToPoint:tempPoint];
            } else {
                [path addLineToPoint:tempPoint];
            }
        }
        
        [path closePath];
        
        CGFloat red = arc4random() % 256;
        CGFloat green = arc4random() % 256;
        CGFloat blue = arc4random() % 256;
        CAShapeLayer *shaper = [CAShapeLayer layer];
        shaper.path = path.CGPath;
        shaper.lineWidth = _valueWidth;
        //如果有顏色就採用用戶賦值的顏色;如果沒有顏色就隨機顏色
        if (self.valueFillColors.count == self.values.count) {
            shaper.fillColor = self.valueFillColors[i].CGColor;
        } else {
            shaper.fillColor = [UIColor colorWithRed:red/256.0 green:green/256.0 blue:blue/256.0 alpha:0.4].CGColor;
        }
        if (self.valueStrokeColors.count == self.values.count) {
            shaper.strokeColor = self.valueStrokeColors[i].CGColor;
        } else {
            shaper.strokeColor = [UIColor colorWithRed:red/256.0 green:green/256.0 blue:blue/256.0 alpha:0.7].CGColor;
        }
        [self.layer addSublayer:shaper];
        
        [self.layerArray addObject:shaper];
    }
}

4、添加頂點處文案:

- (void)initLabel
{
    //移除之前的label
    for (UILabel *label in self.labelArray) {
        [label removeFromSuperview];
    }
    [self.labelArray removeAllObjects];
    
    //label的高度這裏寫死,可以根據需求設置寬度
    CGFloat labelH = 20.0;
    CGFloat fontSize = 15.0;
    CGFloat space = 5.0;
    for (NSInteger i = 0; i < _textArray.count; i++) {
        CGPoint point = [self.outerLayerPoints[i] CGPointValue];
        CGFloat width = [self sizeOfStringWithMaxSize:CGSizeMake(MAXFLOAT, labelH) textFont:fontSize aimString:_textArray[i]].width;
        UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, width, labelH)];
        titleLabel.font = [UIFont systemFontOfSize:fontSize];
        titleLabel.textColor = [[UIColor grayColor] colorWithAlphaComponent:0.6];
        titleLabel.text = _textArray[i];
        [self addSubview:titleLabel];
        switch (i) {
            case 0:
                titleLabel.center = CGPointMake(point.x, point.y - labelH / 2 - space);
                break;
            case 1:
                titleLabel.center = CGPointMake(point.x, point.y - labelH / 2 - space);
                break;
            case 2:
                titleLabel.center = CGPointMake(point.x + width / 2 + space, point.y);
                break;
            case 3:
                titleLabel.center = CGPointMake(point.x, point.y + labelH / 2 + space);
                break;
            case 4:
                titleLabel.center = CGPointMake(point.x, point.y + labelH / 2 + space);
                break;
            case 5:
                titleLabel.center = CGPointMake(point.x - width / 2 -space, point.y);
                break;
            default:
                break;
        }
        [self.labelArray addObject:titleLabel];
    }
}

5、外部調用方式:

- (HexagonBerizeView *)hexagonView
{
    if (_hexagonView == nil) {
        _hexagonView = [[HexagonBerizeView alloc] initWithFrame:CGRectMake(0, 64, ScreenWidth, 300)];
        _hexagonView.textArray = @[@"客戶關係",@"產品",@"價格",@"品牌",@"服務",@"其他"];;
        _hexagonView.valueFillColors = @[[UIColor colorWithRed:7/ 256.0 green:71/256.0 blue:151 / 256.0 alpha:0.4],[UIColor colorWithRed:255/ 256.0 green:0/256.0 blue:0 / 256.0 alpha:0.4]];
        _hexagonView.valueStrokeColors = @[[UIColor colorWithRed:7/ 256.0 green:71/256.0 blue:151 / 256.0 alpha:0.7],[UIColor colorWithRed:255/ 256.0 green:0/256.0 blue:0 / 256.0 alpha:0.7]];
        //這裏最大值取100 (根據需求而定)
        _hexagonView.values = @[@[@(arc4random() % 100),@(arc4random() % 100),@(arc4random() % 100),@(arc4random() % 100),@(arc4random() % 100),@(arc4random() % 100)],
                                  @[@(arc4random() % 100),@(arc4random() % 100),@(arc4random() % 100),@(arc4random() % 100),@(arc4random() % 100),@(arc4random() % 100)]];
    }
    return _hexagonView;
}
[self.view addSubview:self.hexagonView];

Demo下載地址:https://github.com/MichaelSSY/HexagonBerizeView

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章