本篇记录星级评分组件的创建过程以及CALayer的运用。

为了实现一个星级评分的组件,使用了CALayer,涉及到mask、CGPathRef、UIBezierPath、动画和一个计算多角星关键节点的算法。

CALayer管理基于图像的内容,并让我们可以在内容上添加动画。UIView及其子类拥有一个属性layer,我们可以运用该属性做出非常多的效果。例如圆角、多边形、甚至自定义形状的view,局部遮挡,擦除模糊效果,局部内容依次闪亮效果,弧形进度条等等。

首先查看CALayer的一个属性mask,这个属性也是CALayer类型,跟他的名字意义一样,就是用来遮挡内容的,默认为nil,所以我们可以看到一个UIView上的内容。如果给一个可见内容的view的layer属性赋值CALayer实例,那这个view立刻就“消失”了。如下代码:

CALayer *maskLayer = [CALayer layer];

maskLayer.frame = testView.bounds;

self.layer.mask = maskLayer;

再看另一个类CAShapeLayer,继承与CALayer,并具有众多额外属性,例如path、fillColor、strokeColor。其中的path属性,可以决定“不遮挡”路径范围内的内容。如果我们将之前的mask属性赋值为一个CAShapeLayer实例,或者在之前的CALayer实例上addSublayer该实例,并指定一个中心圆的路径,那就实现了常见的圆形头像显示效果。与设置UIView的layer的corner作用一样。

需要注意一点,如果添加的CAShapeLayer的fillColor为[UIColor clearColor].CGColor,即使在path范围内,也将失去“不遮挡”作用,而该值默认为不透明黑色。简单来说,如果透明就将遮挡内容。可以发现,mask及其sublayer上的填充和路径颜色,都是不会显示的,只用于决定是否遮挡自身所在layer的内容。

利用这一特点,可以实现“开门”和“关门”的类似效果,在一个CALayer上添加两个sublayer作为“门”。还可以实现泼墨等更多的效果。

值得一提的是,每个sublayer也可以利用frame和mask属性做出相对于view的局部的遮挡效果。

回到CAShapeLayer的fillColor属性,该属性将path范围内填充上某种颜色;strokeColor是指定path轨迹上的颜色,strokeStart、strokeEnd指定轨迹的开始和结束的绘制点;还有line相关的属性指定线的属性。运用这些属性,可以做出任意形状的进度条、加载动画等。

从以上记录可以看出path属性很重要,该属性是CGPathRef类型。常用创建方法为 CGPathCreateMutable(void)系列方法:

    CGMutablePathRef path = CGPathCreateMutable();
CGPoint firstPoint = [[keyPointsArray firstObject] CGPointValue];
CGPathMoveToPoint(path, nil, firstPoint.x, firstPoint.y); for (int i = ; i < keyPointsArray.count; i++) {
CGPoint currentPoint = [keyPointsArray[i] CGPointValue];
CGPathAddLineToPoint(path, nil, currentPoint.x, currentPoint.y);
} CGPathAddLineToPoint(path, nil, firstPoint.x, firstPoint.y); _maskLayer.path = path;
_starShapeLayer.path = path;
CGPathRelease(path);

也可以使用UIBezierPath的系列类方法,添加好路径后,由方法- (CGPathRef)CGPath得到path。例如:

    UIBezierPath *bezierPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(_bgShapeLayer.frame.size.width / 2.0, _bgShapeLayer.frame.size.height / 2.0) radius:_radius startAngle: endAngle:  * M_PI clockwise:YES];
_bgShapeLayer.path = bezierPath.CGPath;

下面开始记录星级评分组件的创建。

下图为最终效果:

点击星星的左右半边会决定填充一半还是全部内容。还可以设置百分比填充内容。支持>5角星。

单个星星的内部设计为:view设置了灰色背景色;view.layer.mask的path为五角星;view.layer添加一个bgShapeLayer,背景为黄色,控制其width实现填充百分比;再添加一个starShapeLayer,path为同样的五角星,设置路径颜色作为边框。

这里的难点在于计算多角星的交点,如果为五角星,则需要10个关键点的坐标。

下面记录求交点的算法:

1.先确定一个初始顶点

2.根据等分弧度和半径,由三角函数的正余弦相关公式求得全部外顶点

3.求出内交点的两条相交直线

4.由二元一次方程求出交点

具体实现代码如下:

- (NSArray *)getStarKeyPoints
{
CGPoint center = CGPointMake(self.frame.size.width / 2.0, _radius);
CGFloat sectionAngle = * M_PI / _topPointCount; NSMutableArray *keyPointsArray = [NSMutableArray array];
CGPoint firstPoint = CGPointMake(center.x, );
[keyPointsArray addObject:[NSValue valueWithCGPoint:firstPoint]]; //外围顶点
for (int i = ; i < _topPointCount; i++) {
CGFloat x = cosf(i * sectionAngle - M_PI_2) * _radius;
CGFloat y = sinf(i * sectionAngle - M_PI_2) * _radius; CGPoint point = CGPointMake(x + center.x, y + center.y); [keyPointsArray addObject:[NSValue valueWithCGPoint:point]];
} //内交点
NSMutableArray *crossPointsArray = [NSMutableArray array]; //采用二元一次方程求解
//AC点确定直线方程y = kx + b
//过B点直线y = B.y
for (int i = ; i < _topPointCount; i++) {
CGPoint A = [keyPointsArray[i] CGPointValue]; NSInteger index = i + ;
if (index > _topPointCount - ) {
index -= _topPointCount;
}
CGPoint B = [keyPointsArray[index] CGPointValue]; index = i + ;
if (index > _topPointCount - ) {
index -= _topPointCount;
}
CGPoint C = [keyPointsArray[index] CGPointValue]; index = i - ;
if (index < ) {
index += _topPointCount;
}
CGPoint E = [keyPointsArray[index] CGPointValue]; CGFloat F_x = 0.0, F_y = 0.0, k1 = 0.0, k2 = 0.0, b1 = 0.0, b2 = 0.0; if (A.x == C.x) {
F_x = A.x;
} else {
k1 = (A.y - C.y) / (A.x - C.x);
b1 = A.y - k1 * A.x;
} if (B.x == E.x) {
F_x = B.x;
} else {
k2 = (B.y - E.y) / (B.x - E.x);
b2 = B.y - k2 * B.x;
} if (A.x == C.x) {
F_y = k2 * F_x + b2;
}else if (B.x == E.x) {
F_y = k1 * F_x + b1;
}else{
if (k1 == ) {
F_y = A.y;
F_x = (F_y - b2) / k2;
} else {
F_y = (b1 * k2 - b2 * k1) / (k2 - k1);
F_x = (F_y - b1) / k1;
}
} CGPoint pointF = CGPointMake(F_x, F_y);
[crossPointsArray addObject:[NSValue valueWithCGPoint:pointF]];
} //合并数据
for (int i = ; i < crossPointsArray.count; i++) {
[keyPointsArray insertObject:crossPointsArray[i] atIndex:(i * + )];
} return keyPointsArray;
}

将关键点线段添加到path中,生成多角形路径,用于mask和边框layer。

最后添加点击手势,根据触摸点的范围确定操作。

实现单个五角星以后,常见的将五个五角星并排放置,统一管理每个五角星的填充百分比。

ALWStarComment已在Base项目中更新:https://github.com/ALongWay/base.git

干货之运用CALayer创建星级评分组件(五角星)的更多相关文章

  1. Angular 星级评分组件

    一.需求演变及描述: 1. 有一个“客户对公司的总体评价”的字段(evalutation).字段为枚举类型,0-5,对应关系为:0-暂无评价,1-很差,2-差,3-一般,4-好,5-很好 2. 后来需 ...

  2. vue星级评分组件

    <template> <div class="Rating-gray"> <i v-for="(item,index) in itemCla ...

  3. ExtJS 4.2 评分组件

    上一文章是扩展ExtJS自带的Date组件.在这里将创建一个评分组件. 目录 1. 介绍 2. 示例 3. 资源下载 1. 介绍 代码参考的是 Sencha Touch 2上的一个RatingStar ...

  4. js实现星级评分效果(非常规5个li代码)

    1. 前言 此方案受到JS单行写一个评级组件启发,自己写了一个简单Demo. 功能有正常滑动,动态显示实心星星个数:当点击确认,则保持当前的实心星星个数:再移动时未点击,则离开后还是保持之前的状态. ...

  5. JS原生评分组件

    JS原生评分组件 <html> <head> <meta http-equiv="Content-Type" content="text/h ...

  6. 原生JS结合cookie实现商品评分组件

    开发思路如下: 1.利用JS直接操作DOM的方式开发商品评分组件,主要实现功能有:显示评价评分的样式以及将用户操作后对应的数据返回到主页面 2.主页面引入商品评分组件的js文件并根据规定格式的数据,生 ...

  7. 原生JS实现-星级评分系统

    今天我又写了个很酷的实例:星级评分系统(可自定义星星个数.显示信息) sufuStar.star();使用默认值5个星星,默认信息 var msg = [........]; sufuStar.sta ...

  8. javascript星级评分(多个)

    JS打多个类型星级评分: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http: ...

  9. iOS开发UI篇—CAlayer(创建图层)

    iOS开发UI篇—CAlayer(创建图层) 一.添加一个图层 添加图层的步骤: 1.创建layer 2.设置layer的属性(设置了颜色,bounds才能显示出来) 3.将layer添加到界面上(控 ...

随机推荐

  1. Android Studio的git功能的使用介绍

    本文介绍Android Studio(下面简称AS)中git工具的一些简单使用.因为AS为git的使用提供了很多人性化的图形界面操作,在很大程度上可以增加开发效率.本文面向新手,题主自己也是新手一枚, ...

  2. Emit学习(4) - Dapper解析之数据对象映射(二)

    承接着上一篇, 这一篇主要以堆栈的方式来演示一下, db数据转换到类中去的一个过程. 一.先看第一张图 程序在运行到176行(上一篇贴出的代码)的时候, 就会出现上图中的第一个栈. 那在此之前, Da ...

  3. 从客户端中检测到有潜在危险的 Request.Form 值 --MVC

    可以在处理Post方法的Action添加一个特性: [ValidateInput(false)],这样处理就更加有针对性,提高页面的安全性. 如: [HttpPost][ValidateInput(f ...

  4. 如何在MVC_WebAPI项目中的APIController帮助页面添加Web测试工具测试

    本文转载自:http://www.cnblogs.com/pmars/p/3673811.html 先看效果图: 以下是原文: 如何在帮助页面添加测试工具 上一篇我在ASP.NET里面添加了一个Hel ...

  5. 怎样解决PowerDesigner15出现许可证过期问题?

    今天打开PowerDesigner软件,出现许可证过期问题,怎样解决 1)打开软件安装路径(通过搜索打开文件所在路径或通过软件图标的属性打开)

  6. 非阻塞同步算法与CAS(Compare and Swap)无锁算法

    锁(lock)的代价 锁是用来做并发最简单的方式,当然其代价也是最高的.内核态的锁的时候需要操作系统进行一次上下文切换,加锁.释放锁会导致比较多的上下文切换和调度延时,等待锁的线程会被挂起直至锁释放. ...

  7. Java一步一步构建web系统 在IDEA下用Maven搭建多模块项目

    1.需求 做一个项目会有很多模块,主要是方便复用,通过各个模块之间聚合.模块也可以独立出来,如公用类库,也可以在做其它项目中使用.该文的实例会有两个模块:分别为dallin-web模块,dallin- ...

  8. GJM:用C#实现网络爬虫(一) [转载]

    网络爬虫在信息检索与处理中有很大的作用,是收集网络信息的重要工具. 接下来就介绍一下爬虫的简单实现. 爬虫的工作流程如下 爬虫自指定的URL地址开始下载网络资源,直到该地址和所有子地址的指定资源都下载 ...

  9. java1.8的默认方法的坑

    默认方法: 接口的方法一直都是抽象方法,自从1.8出来了之后,新增了一个默认方法.可以在接口中实现方法 1.默认方法需要用default修饰 2.默认方法不能是静态的 3.子接口继承了2个相同签名的默 ...

  10. css3中的前缀

    css3中: -o-:opera -moz:firefox -webkit:safari chrome -ms:IE9