iOS绘图CALayer、UIBezierPath运用(边框、填充、复制、渐变)

一.动态折线图效果图
iOS绘图CALayer、UIBezierPath运用(边框、填充、复制、渐变)_第1张图片
1.首先绘制网格和坐标 CAReplicatorLayer
    // 添加网格图层
    // 网格列线
    CAReplicatorLayer *rowReplicatorLayer = [ CAReplicatorLayer new ];
    _xReplicatorLayer = rowReplicatorLayer;
    rowReplicatorLayer. position = CGPointMake ( 0 , 0 );
    CALayer *rowBackLine = [ CALayer new ];
    _xBackLine = rowBackLine;
    [rowReplicatorLayer addSublayer :rowBackLine];
    [mainView. layer addSublayer :rowReplicatorLayer];
    // 网格横线
    CAReplicatorLayer *columnReplicatorLayer = [ CAReplicatorLayer new ];
    _yReplicatorLayer = columnReplicatorLayer;
    columnReplicatorLayer. position = CGPointMake ( 0 , 0 );
    CALayer *columnBackLine = [ CALayer new ];
    _yBackLine = columnBackLine;
    [columnReplicatorLayer addSublayer :columnBackLine];
    [mainView. layer addSublayer :columnReplicatorLayer];

    //图层绘制动画
    [ CATransaction begin ];
    [ CATransaction setAnimationDuration : 0 ];
    CGFloat rowSpacing = _heightGrid ;
    CGFloat columnSpacing = _widthGrid ;
    // 坐标轴这里我简单的使用label循环创建
    _XLabelView . frame = CGRectMake ( _YLabelWidth - _widthGrid / 2 , _mainView . frame . size . height - _XLabelHeight , _mainView . frame . size . width - _YLabelWidth , _XLabelHeight );
    _YLabelView . frame = CGRectMake ( 0 , 0 - _heightGrid / 2 + _YunitLabelHeight , _YLabelWidth , _mainView . frame . size . height - _XLabelHeight );
    // 图层复制(设置复制的数量)
    _xReplicatorLayer . instanceCount = _numberY + 1 ;
    _yReplicatorLayer . instanceCount = _valueData . count ;
   
    _xReplicatorLayer . instanceTransform = CATransform3DMakeTranslation ( 0 , rowSpacing + _widthLine , 0 );
    _yReplicatorLayer . instanceTransform = CATransform3DMakeTranslation (columnSpacing + _widthLine , 0 , 0 );
   //设置图层大小
    _yReplicatorLayer . frame = _xReplicatorLayer . frame = CGRectMake ( _YLabelWidth , _YunitLabelHeight , _mainView . frame . size . width - _YLabelWidth - _spaceWidth , _mainView . frame . size . height - _XLabelHeight - _YunitLabelHeight );
   
    _yBackLine . frame = CGRectMake ( 0 , 0 , _widthLine , _yReplicatorLayer . frame . size . height );
    _xBackLine . frame = CGRectMake ( 0 , 0 , _yReplicatorLayer . frame . size . width , _widthLine );
[ CATransaction commit ];

2.绘制曲线和折点
    // 曲线
    _curveLineLayer . strokeColor = _curveLineColor . CGColor ;
    _curveLineLayer . lineWidth = _curveLineWidth ;

    CAShapeLayer *curveLineLayer = [ CAShapeLayer new ];
    _curveLineLayer = curveLineLayer;
    curveLineLayer. fillColor = nil ;
    curveLineLayer. lineJoin = kCALineJoinRound ;
    [mainView. layer addSublayer :curveLineLayer];
/**
  将数值转换成坐标
 */
-( CGPoint )_changeValueToPoint:( NSDictionary *)data{
    CGFloat xValue = [data[ JHChartViewX ] floatValue ];
    CGFloat yValue = [data[ JHChartViewY ] floatValue ];
    //x 坐标等于 value* 宽度
    //y 坐标等于 value/ 最大值 *y 高度
    CGPoint point = CGPointMake ( _YLabelWidth + xValue *( _widthGrid + _widthLine ), _YunitLabelHeight +( 1 -yValue/ _maxY )* _yBackLine . frame . size . height );
    return point;
   
}
/**
  生成坐标点
 */
-( void )_creatPoint{
    _pointData = @[] . mutableCopy ;
    for ( NSDictionary *dict in _valueData ) {
//转换成当前坐标点
        CGPoint point = [ self _changeValueToPoint :dict];
        [ _pointData addObject :[ NSValue valueWithCGPoint :point]];
    }
    CGFloat sum = 0 ;
    // 计算总长度
    for ( int i = 0 ; i< _pointData . count - 1 ; i++) {
      CGPoint p1 = [ _pointData [i] CGPointValue ];
      CGPoint p2 = [ _pointData [i+ 1 ] CGPointValue ];
        CGFloat temp = sqrt ( pow ((p1. x -p2. x ), 2 )+ pow ((p1. y -p2. y ), 2 ));
        sum += temp;
    }
    _sumLineWidth = sum;
}
/**
  生成路径
 */
-( void )_creatPath{
 
    [ self _creatPoint ];
    UIBezierPath * path = [ UIBezierPath bezierPath ];
    UIBezierPath *backPath = [ UIBezierPath bezierPath ];
    CGPoint firstPoint = [ _pointData [ 0 ] CGPointValue ];
    CGPoint lastPoint = [ _pointData [ _pointData . count - 1 ] CGPointValue ];
    [path moveToPoint :firstPoint];
    [backPath moveToPoint : CGPointMake (firstPoint. x , _yBackLine . frame . size . height + _YunitLabelHeight )];
    for ( NSValue *pointValue in _pointData ) {
        CGPoint point = [pointValue CGPointValue ];
        if (pointValue == _pointData [ 0 ]) {
            [backPath addLineToPoint :point];
            continue ;
        }
        [backPath addLineToPoint :point];
        [path addLineToPoint :point];
    }
    [backPath addLineToPoint : CGPointMake (lastPoint. x , _yBackLine . frame . size . height + _YunitLabelHeight )];
    _path = path;
//背景路径
    _backPath = backPath;
   
}
/**
  绘制坐标点
 
 @param point 坐标点
 @param index 标记 tag
 */
- ( void )drawPoint:( CGPoint )point withIndex:( NSInteger )index{
}

3.绘制背景
// 封闭阴影
    CAShapeLayer * backLayer = [ CAShapeLayer new ];
    _backLayer = backLayer;
    [mainView. layer addSublayer :backLayer];
   
    CAShapeLayer *progressLayer = [ CAShapeLayer layer ];
    _progressLayer = progressLayer;
    [ _backLayer setMask :progressLayer];
    // 背景
    _backLayer . fillColor = _fillLayerBackgroundColor . CGColor ;
    _backLayer . hidden = _fillLayerHidden ;
    // 背景路劲
    _backLayer . path = _backPath . CGPath ;
// 背景移动遮罩
    CGFloat lineWidth = _yReplicatorLayer . frame . size . height + _YunitLabelHeight ;
    _progressLayer . lineWidth = lineWidth* 2 ;
    _progressLayer . lineCap = kCALineCapSquare ;
    _progressLayer . strokeColor = [ UIColor whiteColor ]. CGColor ;
    // 将路径填充颜色设置为透明
    _progressLayer . fillColor = [ UIColor redColor ]. CGColor ;
4.添加动画效果
    4.1曲线绘制动画
CABasicAnimation *pointAnim = [ CABasicAnimation animationWithKeyPath : @"strokeEnd" ];
        pointAnim. fromValue = @0.0 ;
        pointAnim. toValue = @1.0 ;
        pointAnim. duration = _drawAnimationDuration ;
        [ _curveLineLayer addAnimation :pointAnim forKey : @"drawLine” ];

#warning 背景是默认全部填充的,无法像曲线一样移动,故采用 setMask: 方法,给它加上一个遮罩。利用遮罩的绘制动画,模拟出背景的动画
  // 图层直线的轨迹
        UIBezierPath *path = [ UIBezierPath bezierPath ];
#warning 起始点似乎有问题
        [path moveToPoint : CGPointMake (- _widthGrid * 2 , 0 )];
        [path addLineToPoint : CGPointMake ( _yReplicatorLayer . frame . size . width , 0 )];
        _progressLayer . path = path. CGPath ;
        _progressLayer . strokeEnd = 0.0 ;
        // 动画时间
        CGFloat duration = _drawAnimationDuration *( _sumLineWidth / _yReplicatorLayer . frame . size . width );
        // 进度程度
        CGFloat progress = 1.0 ;
        //strokeEnd 动画到某个点结束
        CABasicAnimation *animate = [ CABasicAnimation animationWithKeyPath : @"strokeEnd" ];
        animate. removedOnCompletion = NO ;
        animate. fillMode = kCAFillModeForwards ;
        animate. duration = duration;
        animate. fromValue = @0.0 ;
        animate. toValue = @( progress ) ;
        // 为图层添加动画
        [ _progressLayer addAnimation :animate forKey : @"drawProgress” ];
二.动态渐变色百分比移动图效果图
效果图

1.绘制渐变色曲线 CAGradientLayer
// 将渐变图层添加到 animationView 的图层上
    [ self . animationView . layer addSublayer : self . gradientLayer ];
    [ self . gradientLayer addSublayer : self . gradientColorLayer ];
_gradientLayer = [ CALayer layer ];
_gradientColorLayer = [ CAGradientLayer layer ];
  _gradientLayer . frame = CGRectMake ( 0 , 0 , _animationView . frame . size . width , _animationView . frame . size . height );
    _gradientColorLayer . frame = CGRectMake ( 0 , 0 , _animationView . frame . size . width , _animationView . frame . size . height );
    _gradientColorLayer . cornerRadius = _gradientLayer . frame . size . height / 2 ;
        [ _gradientColorLayer setColors :[ NSArray arrayWithObjects :( id ) _startColor . CGColor ,( id ) _endColor . CGColor , nil ]];
        // 渐变方向水平
        [ _gradientColorLayer setStartPoint : CGPointMake ( 0 , 1 )];
2.添加蒙版 CAShapeLayer
CGFloat lineWidth = progressHeight ;
       
        _progressLayer = [ CAShapeLayer layer ];
       
        _progressLayer . lineWidth = lineWidth* 2 ;
        _progressLayer . lineCap = kCALineCapSquare ;
        _progressLayer . strokeColor = [ UIColor whiteColor ]. CGColor ;
        // 将路径填充颜色设置为透明
        _progressLayer . fillColor = [ UIColor clearColor ]. CGColor ;
  // progressLayer 来截取渐变层
    [ self . gradientLayer setMask : self . progressLayer ];
3.绘制百分比向下箭头Label
/**
  绘制向下的三角形(这里的路径超出了Label,当然我们只要不截取超出部分就行了,不用管原始Label中的文字是否居中)
 */
-( void )drawTriangle{
    // 圆角矩形
    UIBezierPath *path = [ UIBezierPath bezierPathWithRoundedRect : _labPercent . frame cornerRadius : 3 ];
    // 三角形
    UIBezierPath *trianglePath = [[ UIBezierPath alloc ] init ];
    [trianglePath moveToPoint : CGPointMake ( _percentView . frame . size . width / 2 - 2 , _percentView . frame . size . height - 5 )];;
    [trianglePath addLineToPoint : CGPointMake ( _percentView . frame . size . width / 2 , _percentView . frame . size . height )];
    [trianglePath addLineToPoint : CGPointMake ( _percentView . frame . size . width / 2 + 2 , _percentView . frame . size . height - 5 )];
    // 扩展绘制路径
    [path appendPath :trianglePath];
    CAShapeLayer *fillLayer = [ CAShapeLayer layer ];
    fillLayer. path = path. CGPath ;
    fillLayer. fillColor = kBaseColor . CGColor ;
  #warning 必须将他添加到最下一层,否则会遮挡 其他图层
//    [_labPercent.layer addSublayer:fillLayer];
    [ _labPercent . layer insertSublayer :fillLayer atIndex : 0 ];
}
4.动画
// 图层直线的轨迹
    UIBezierPath *path = [ UIBezierPath bezierPath ];
    [path moveToPoint : CGPointMake ( 0 , 0 )];
    [path addLineToPoint : CGPointMake ( _animationView . frame . size . width , 0 )];
    _progressLayer . path = path. CGPath ;

    self . progressLayer . strokeEnd = 0.0 ;
    // 动画时间
    CGFloat duration = _animationTime ;
    // 进度程度
    CGFloat progress = _percent ;
    //strokeEnd 动画到某个点结束
    CABasicAnimation *animate = [ CABasicAnimation animationWithKeyPath : @"strokeEnd" ];
    animate. removedOnCompletion = NO ;
    animate. fillMode = kCAFillModeForwards ;
    animate. duration = duration;
    animate. fromValue = @0.0 ;
#warning 存在偏差,目前不知道原因
    animate. toValue = @( progress- 0.02) ;
    // 为图层添加动画
    [ self . progressLayer addAnimation :animate forKey : @"anim1" ];
   
    // 百分比标签动画( iOS 10在layoutSubView中 失效???)
     _percentView . center = CGPointMake ( 0 , _percentView . center . y );
#warning 此处使用延迟,使动画生效了
    dispatch_after ( dispatch_time ( DISPATCH_TIME_NOW , ( int64_t )( 0.01 * NSEC_PER_SEC )), dispatch_get_main_queue (), ^{
        [ UIView animateWithDuration : _animationTime animations :^{
           
            _percentView . center = CGPointMake ( self . frame . size . width * _percent , _percentView . center . y );
        }];
    });

你可能感兴趣的:(iOS开发记录)