iOS充电效果实现延伸

2022年1月5日更新:

项目地址

image.png

现在新能源汽车越来越普及.相应的充电桩也越来越多.所以看着实现了两种充电效果

一.充电效果1

image.png

这种比较简单.使用CAShapeLayer的mask属性.放置两层背景图.一层使用mask蒙层获取新的图层.就达到以上效果

/* A layer whose alpha channel is used as a mask to select between the
 * layer's background and the result of compositing the layer's
 * contents with its filtered background. Defaults to nil. When used as
 * a mask the layer's `compositingFilter' and `backgroundFilters'
 * properties are ignored. When setting the mask to a new layer, the
 * new layer must have a nil superlayer, otherwise the behavior is
 * undefined. Nested masks (mask layers with their own masks) are
 * unsupported. */

**@property**(**nullable**, **strong**) **__kindof** CALayer *mask;

二.充电效果2

image.png

这种合理使用CAReplicatorLayer类.使用这个类的一个好处是它有一个instanceTransform属性.可以递进形变

/* The matrix applied to instance k-1 to produce instance k. The matrix
 * is applied relative to the center of the replicator layer, i.e. the
 * superlayer of each replicated sublayer. Defaults to the identity
 * matrix. Animatable. */
 **@property** CATransform3D instanceTransform;
实现分为:

1.创建36个CAReplicatorLayer图层.

2.每个CAReplicatorLayer均有少量的偏移/缩放/以及旋转

3.根据进度更新每个CAReplicatorLayer的颜色值instanceColor.

核心代码如下:

//首先需要点图

-(CAReplicatorLayer *)getImageItemView{

  UIView *dotView = [[UIView alloc]initWithFrame:CGRectMake(**self**.dotFlagXY, **self**.dotFlagXY, **self**.dotWH, **self**.dotWH)];//这个是最大值

  dotView.backgroundColor = UIColor.whiteColor;

  dotView.layer.cornerRadius = **self**.dotWH * 0.5;

  CAReplicatorLayer *replLayer = [CAReplicatorLayer layer];

  [replLayer addSublayer:dotView.layer];

  replLayer.backgroundColor = UIColor.clearColor.CGColor;

  replLayer.frame = CGRectMake(0, 0, **self**.sumLayerWH, **self**.sumLayerWH);//这个是其大小

  replLayer.position = CGPointMake(**self**.sumLayerWH * 0.5, **self**.sumLayerWH * 0.5);//中心点

  replLayer.instanceColor = UIColor.whiteColor.CGColor;

  /// 设置复制次数

  replLayer.instanceCount = **self**.itemCount;

  CATransform3D transform = CATransform3DIdentity;

  // 偏移

  transform = CATransform3DTranslate(transform, **self**.itemOffX, **self**.itemOffY, 0);

  // 缩放

  transform = CATransform3DScale(transform, **self**.itemScale, **self**.itemScale, 0);

  //旋转

  transform = CATransform3DRotate(transform, -0.1, 0, 0, -1);

  replLayer.instanceTransform = transform;

  return  replLayer;

}

三.最后一个用于统计的效果

圆环
效果描述:

整个圆环分为四个弧度,每段弧度均有渐变.而且每段弧度均会发生变化

一:关于实现.

a.尝试:也是做了很多尝试.使用mask做渐变.这种每次都要计算角度.以及渐变的偏移值.最终还实现不了效果.遂放弃

b.翻阅资料.找到苹果自带线性渐变函数CGContextDrawLinearGradient

/* Fill the current clipping region of `context' with a linear gradient from

`startPoint' to `endPoint'. The location 0 of `gradient' corresponds to

`startPoint'; the location 1 of `gradient' corresponds to `endPoint';

colors are linearly interpolated between these two points based on the

values of the gradient's locations. The option flags control whether the

gradient is drawn before the start point or after the end point. */

CG_EXTERN **void** CGContextDrawLinearGradient(CGContextRef cg_nullable c,CGGradientRef cg_nullable gradient, CGPoint startPoint, CGPoint endPoint,CGGradientDrawingOptions options)CG_AVAILABLE_STARTING(10.5, 2.0);

以上方法主要参数分别是提供上下文.颜色渐变对象,渐变开始点,渐变结束点.options默认使用0

主要思路如下:

1.开启图形上下文准备绘制

UIGraphicsBeginImageContextWithOptions(CGSizeMake(2*radius + width, radius * 2 + width),**NO**,[UIScreen mainScreen].scale); //开始画线

2.设置圆角,弧度,弧形长条的宽度

CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineJoin(context, kCGLineJoinRound);
CGContextSaveGState(context);
CGContextSetLineWidth(context, width);

3.设置渐变对象

CGGradientRef gradientRef = CGGradientCreateWithColors(colorSpace, (**__bridge** CFArrayRef)colorArray, **NULL**);

4.根据开始角度和结束角度.计算在图形上的位置

//获取当前坐标的开始和结束点
-(CGPoint)calcCircleCoordinateWithCenter:(CGPoint) center andWithAngle : (CGFloat) angle andWithRadius: (CGFloat) radius{
 CGFloat x2 = radius*cosf(angle*M_PI/180);
CGFloat y2 = radius*sinf(angle*M_PI/180);
return  CGPointMake(center.x+x2, center.y+y2);
}

5.使用线性渐变控制渐变方向

// 控制渐变方向的
CGContextDrawLinearGradient(context, gradientRef,endPoint, startPoint,0);

6.获取图形上下文的图片

UIImage *theImage = UIGraphicsGetImageFromCurrentImageContext();
二:关于优化.
1.线性渐变以及带圆角的情况.会有被裁掉的情况.效果如下:
image.png

这是因为渐变是线性的.所以圆角的部分会被裁掉.针对这个问题.最终的解决方案是将弧度缩进一点.并且将渐变延长一点.

//弧度缩进一点.因为有圆角和半径宽度
CGFloat offetMargin = 0;// M_PI * 10 / 180;
CGContextAddArc(context, radius + width * 0.5, radius + width * 0.5, radius, startAngle + offetMargin , endAngle - offetMargin, 0);
2.关于数据的优化.

毕竟这个圆环是和数据绑定.会有变化.如果遇到特定数据我们需要进行优化,例如[360,0,0,0]极端数据.效果肯定很难看

所以我们把每一个段的弧度控制在[72-144]度区间.保障每一种颜色都有一定的弧度展示出来

/// 此函数为了防止数据异常.例如[360.0.0.0]界面效果不美观的优化.返回优化后的数据[144.72.72.72]

/// **@param** values 原始数据
-(NSArray *)changeValuesWithArr:(NSArray *)values{
 //平均分到每个数据为
 NSMutableArray * angleValues = [NSMutableArray arrayWithArray:values];
 NSNumber *sum = [angleValues valueForKeyPath:@"@sum.self"];
 CGFloat itemBaseValue = sum.floatValue * 0.8 / values.count;
 NSMutableArray * tempArr = [NSMutableArray array];
 NSInteger count = 0;
 for  (NSNumber * itemValue  in  angleValues) {
    [tempArr addObject:@(itemValue.floatValue * 0.2 + itemBaseValue)];
     count ++;
     }
 angleValues = tempArr;
return  angleValues.copy;
}
3.关于渐变效果的优化.

系统有提供渐变度控制函数:CGGradientCreateWithColorComponents

/* Creates a gradient by pairing the color components provided in

 `components' with locations provided in `locations'. If `locations' is

 NULL, the first color in `colors' will be at location 0, the last color

 in `colors' will be at location 1, and intervening colors will be at

 equal intervals in between. Otherwise, each location in `locations'

 should be a CGFloat between 0 and 1\. Each set of color components should

 specify a color in the color space `space' (which may not be a pattern or

 indexed color space). The number of locations is specified by `count';

 the number of color components is the product of `count' and the number

 of color components of `space'. If no color is provided for 0 or 1, the

 gradient will use the color provided at the locations closest to 0 and 1

 for those values. */

CG_EXTERN CGGradientRef **__nullable** CGGradientCreateWithColorComponents(CGColorSpaceRef cg_nullable space,**const** CGFloat * cg_nullable components,**const** CGFloat * **__nullable** locations, size_t count)CG_AVAILABLE_STARTING(10.5, 2.0);

但是我使用效果还是不太好.所以最后根据30度一个颜色.除了最后一个是使用透明色.颜色数据里面全部是指定颜色.通过控制数组的颜色数据来实现渐变效果.这种完美处理了长短不一.尾部的渐变效果异常的情况

//优化展示效果.
CGFloat offsetAngle = endA - beginA;
NSMutableArray * gradientMuArr = [NSMutableArray array];
//每30弧度算一个刻度
int  colorCount = ceilf(offsetAngle / 30.0);    
for ( int  i = 0; i < colorCount - 1; i ++) {
[gradientMuArr addObject:mpiColor];
}
if (gradientMuArr.count == 0) {
[gradientMuArr addObject:mpiColor];
}
[gradientMuArr addObject:bgColor];

以上就是所有分享.希望对你有帮助!


2022年1月5日针对使用多色渐变圆环的补充说明,在实际场景中可能不需要我们自行针对数据进行过滤以及优化.也就是说极端数据如[360,0,0,0],也能正常显示一个圆环,而此时CGContextDrawLinearGradient线性渐变就无法满足了.
我们再次回到最初的设想.设定多段渐变,随后将每段渐变色按照其值进行旋转.也能达到以上效果.

image.png

原理和充电效果1相似.使用CAShapeLayer的mask属性获取渐变效果,蒙版图层如下:

image.png

随后同样实现方式实现四次获取四段圆环,每段圆环递增角度进行旋转拼接,如此实现将也不会受到线性渐变的影响而造成被裁切:

image.png

相关代码如下:
1.设置蒙版图层.

[self.gradientContentLayer addSublayer:gradientLayer]; //设置颜色渐变
[self.gradientContentLayer addSublayer:gradientLayer1];
[self.gradientContentLayer addSublayer:gradientLayer2];
[self.gradientContentLayer setMask:shareLayer];

2.角度旋转

self.gradientContentLayer.transform = CATransform3DRotate(CATransform3DIdentity, (startAngle + M_PI_2), 0, 0, 1);

推荐阅读更多精彩内容