概述

订单评论里实现星级评分控件: 简单整星评价与非整星的精评价.

详细

现在很多应用都有评分功能. 有了订单就有订单评论,订单评论里就有星级评分控件了! 一般来说, 都是简单整星的评价, 但是也有奇葩的小数星评价.

一、程序实现

一. 简单整星评价

实现步骤:

  • 1.创建 imageView, 用来改变星级图片.

  • 2.通过手势来区分点击到的位置.

  • 3.通过位置判断 imageView 显示的图片(一般都是5颗星评价,根据星星点亮颗数进行命名:score1~score5)

创建星星starImage和分数score:

@property (nonatomic, weak) UIImageView *starImage;
@property (nonatomic, copy) NSString *score;

设置starImage:

 UIImageView *starImage = [[UIImageView alloc] init];
starImage.userInteractionEnabled = YES;
starImage.backgroundColor = [UIColor clearColor];
starImage.frame = CGRectMake(CGRectGetMaxX(label.frame) + 15, 0, 80, 14);
starImage.image = [UIImage imageNamed:@"score0"];
[bgView addSubview:starImage];
self.starImage = starImage;

添加星级评价手势:

    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(scoreClick:)];
[starImage addGestureRecognizer:tap];

手势操作结果:

- (void)scoreClick:(UITapGestureRecognizer *)tap {
CGPoint point = [tap locationInView:tap.view];
NSLog(@"%@", NSStringFromCGPoint(point)); if (point.x < self.starImage.width / 5) {
self.starImage.image = [UIImage imageNamed:@"score1"]; // 展示点亮图片
self.score = @"1"; // 评论分数
} else if (point.x < self.starImage.width / 5 * 2) {
self.starImage.image = [UIImage imageNamed:@"score2"];
self.score = @"2";
} else if (point.x < self.starImage.width / 5 * 3) {
self.starImage.image = [UIImage imageNamed:@"score3"];
self.score = @"3";
} else if (point.x < self.starImage.width / 5 * 4) {
self.starImage.image = [UIImage imageNamed:@"score4"];
self.score = @"4";
} else if (point.x < self.starImage.width) {
self.starImage.image = [UIImage imageNamed:@"score5"];
self.score = @"5";
}
}

二. 非整星精评价

上面需求并没有那么细,就评论整颗星星评价, 但是也有新需求半颗星星,这边就先整理下. 同时这次不仅支持非整星精评价, 也支持简单整星评价.

实现步骤:

  • 1. 初始化单个星星的实现, 按百分比裁剪图片改变星星整体图片(一张灰色暗色星星图, 一张高亮色星星图).

  • 2. 初始化星星按钮的布局, 通过触摸事件响应区分点击到的位置, 展现整体评价效果.

  • 3. 对score分数属性和allowFraction是否小数整颗星属性露出展现,在当前控制器里使用代理做分数改变后的操作, 初始化星星评价组件。

Step1. 初始化单个星星的实现

单个星星的实现, 要考虑到全灰状态,全亮状态,百分比高亮状态. 这里用UIButton来实现, 先把图片缩放到和Button大小一样, 可以根据百分比将图像进行裁剪,让新图像的宽度只有百分比所占的整个图像的宽度. 这里自定义ZLStarButton

按百分比裁剪图片:

+ (UIImage *)cutImage:(UIImage *)image fraction:(CGFloat)fractonPart{

    CGFloat width = image.size.width * fractonPart * image.scale;
CGRect newFrame = CGRectMake(0, 0, width , image.size.height * image.scale);
CGImageRef resultImage = CGImageCreateWithImageInRect(image.CGImage, newFrame);
UIImage *result = [UIImage imageWithCGImage:resultImage scale:image.scale orientation:image.imageOrientation];
CGImageRelease(resultImage); return result;
}

暗色置灰的状态设置为UIButton的正常普通状态:

- (void)setDarkImg:(UIImage *)darkImg{

    _darkImg = [ZLStarButton reSizeImage:darkImg toSize:self.frame.size];
[self setImage:_darkImg forState:UIControlStateNormal]; }

全高亮的状态设置为UIButton的点击状态:

- (void)setBrightImg:(UIImage *)brightImg{

    _brightImg = [ZLStarButton reSizeImage:brightImg toSize:self.frame.size];
[self setImage:_brightImg forState:UIControlStateSelected];
}

在BackgroundImage设置为灰色的星星图像,设置为Button的高亮状态:

- (void)setFractionPart:(CGFloat)fractionPart{

    if (fractionPart == 0) {
return;
} UIImage *image = [ZLStarButton cutImage:self.brightImg fraction:fractionPart];
self.imageView.contentMode = UIViewContentModeScaleAspectFit;
self.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
self.contentVerticalAlignment = UIControlContentVerticalAlignmentFill;
[self setImage:image forState:UIControlStateHighlighted];
[self setBackgroundImage:self.darkImg forState:UIControlStateHighlighted];
self.selected = NO;
self.highlighted = YES;
}

如果点击到星星的一部分,把点击点转换成小数部分给上层。C语言的round函数可以四舍五入,这里的10代表保留一位小数。

- (CGFloat)fractionPartOfPoint:(CGPoint)point{

    CGFloat fractionPart =  (point.x - self.frame.origin.x) / self.frame.size.width;
return round(fractionPart * 10) / 10;
}

Step2. 初始化星星按钮的布局, 通过触摸事件ZLStarRatingControl响应区分点击到的位置, 展现整体评价效果.

根据星星的数量逐个添加在视图上,用UIView的tag来标记对应的星星按钮。

- (void)setupView {
for (NSInteger index = 0; index < self.numberOfStars; index++) {
ZLStarButton *starButton = [ZLStarButton.alloc initWithSize:self.starSize];
starButton.tag = index;
starButton.darkImg = self.darkStarImg;
starButton.brightImg = self.brightStarImg;
starButton.userInteractionEnabled = NO;
[self addSubview:starButton];
}
}

计算每个星星的位置及计算间隔和上下边距并layout

- (void)layoutSubviews {
[super layoutSubviews]; for (NSInteger index = 0; index < self.numberOfStars; index ++) {
ZLStarButton *starButton = [self starForTag:index];
CGFloat starY = (self.frame.size.height - self.starSize.height) / 2;
CGFloat margin = 0;
if (self.numberOfStars > 1) {
margin = (self.frame.size.width - self.starSize.width * self.numberOfStars) / (self.numberOfStars - 1);
}
starButton.frame = CGRectMake((self.starSize.width + margin) * index, starY, self.starSize.width, self.starSize.height);
}
}

根据tag和根据点击的CGPoint找到对应的星星按钮:

- (ZLStarButton *)starForPoint:(CGPoint)point {

    for (NSInteger i = 0; i < self.numberOfStars; i++) {
ZLStarButton *starButton = [self starForTag:i];
if (CGRectContainsPoint(starButton.frame, point)) {
return starButton;
}
}
return nil;
}
- (ZLStarButton *)starForTag:(NSInteger)tag { __block UIView *target;
[self.subviews enumerateObjectsUsingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if (obj.tag == tag) {
target = obj;
*stop = YES;
}
}];
return (ZLStarButton *)target;
}

处理触摸事件:

从最后一个星星到某个星星从右到左依次熄灭

- (void)starsDownToIndex:(NSInteger)index {
for (NSInteger i = self.numberOfStars; i > index; --i) {
ZLStarButton *starButton = [self starForTag:i];
starButton.selected = NO;
starButton.highlighted = NO;
}
}

从第一个到某个星星开始从左到右依次点亮

- (void)starsUpToIndex:(NSInteger)index {
for (NSInteger i = 0; i <= index; i++) {
ZLStarButton *starButton = [self starForTag:i];
starButton.selected = YES;
starButton.highlighted = NO;
}
}

ZLStarRatingControl.h 文件设置一个评分的属性score,重写其setter方法. allowFraction,用来判断组件是否需要分数表示. 判断评分的整数部分是否已经亮着,亮着那么说明从左到右最后一个亮着的右边,反之在左边,分别调用从右到左依次熄灭,或从左到右依次点亮的方法,最后再设置分数部分.

- (void)setScore:(CGFloat)score{
if (_score == score) {
return;
}
_score = score;
NSInteger index = floor(score); // floor函数是取最大整数,相当于直接去除小数点后面的数字。
CGFloat fractionPart = score - index; if (!self.isAllowFraction || fractionPart == 0) { // 不允许小数半颗星(取整颗星)
index -= 1;
} ZLStarButton *starButton = [self starForTag:index];
if (starButton.selected || score == 0) { // 当选中或者分数为0则依次熄灭
[self starsDownToIndex:index];
} else { // 当未选中则依次点亮
[self starsUpToIndex:index];
}
starButton.fractionPart = fractionPart;
}

这里面用到UIControl的四个方法:

开始点击的时候的事件处理: 点击时候只要确定点击的星星,确定其小数部分,然后调用评分属性score的setter方法就好了.

- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
CGPoint point = [touch locationInView:self];
ZLStarButton *pressedStar = [self starForPoint:point];
if (pressedStar) {
self.currentStar = pressedStar;
NSInteger index = pressedStar.tag;
CGFloat fractionPart = 1;
if (self.isAllowFraction) { // 允许小数半颗星
fractionPart = [pressedStar fractionPartOfPoint:point];
}
self.score = index + fractionPart;
}
return YES;
}

手指未抬起在屏幕上继续移动的事件处理: 移动处理除了和点击一样的判断逻辑,还要注意手指移开了星星之外的地方,分为所在星星的左边(当前星星熄灭),右边(当前星星完全点亮)两种状态.

- (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {

    CGPoint point = [touch locationInView:self];

    ZLStarButton *pressedStar = [self starForPoint:point];

    if (pressedStar) {
self.currentStar = pressedStar;
NSInteger index = pressedStar.tag;
CGFloat fractionPart = 1;
if (self.isAllowFraction) { // 允许小数半颗星
fractionPart = [pressedStar fractionPartOfPoint:point];
}
self.score = index + fractionPart;
} else { if (point.x < self.currentStar.frame.origin.x) {
self.score = self.currentStar.tag;
} else if (point.x > (self.currentStar.frame.origin.x + self.currentStar.frame.size.width)){
self.score = self.currentStar.tag + 1;
}
}
return YES;
}

离开屏幕处理

- (void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
[super endTrackingWithTouch:touch withEvent:event];
if ([self.delegate respondsToSelector:@selector(starsControl:didChangeScore:)]) {
[self.delegate starsControl:self didChangeScore:self.score];
}
}

因为别的特殊情况事件被结束取消的处理

- (void)cancelTrackingWithEvent:(UIEvent *)event {
[super cancelTrackingWithEvent:event];
if ([self.delegate respondsToSelector:@selector(starsControl:didChangeScore:)]) {
[self.delegate starsControl:self didChangeScore:self.score];
}
}

完成触摸操作时,设置一个代理, 可以用一个回调将当前的评分传给外界。

@protocol ZLStarRatingControlDelegate <NSObject>
@optional
/**
* 回调改变星星评价后的分数
@param starsControl 星星组件
@param score 分数
*/
- (void)starsControl:(ZLStarRatingControl *)starsControl didChangeScore:(CGFloat)score;
@end

初始化星星组件方法:

初始化一个星星评价组件

- (instancetype)initWithFrame:(CGRect)frame
stars:(NSInteger)number
starSize:(CGSize)size
darkStarImg:(UIImage *)darkImg
brightStarImg:(UIImage *)brightImg{ if (self = [super initWithFrame:frame]) {
_numberOfStars = number;
_darkStarImg = darkImg;
_brightStarImg = brightImg;
_starSize = size;
_allowFraction = NO;
self.clipsToBounds = YES;
self.backgroundColor = [UIColor clearColor];
[self setupView];
}
return self;
}

初始化一个星星评价组件,默认5颗星,默认星星的长宽为frame的高度

- (instancetype)initWithFrame:(CGRect)frame
darkStarImg:(UIImage *)darkImg
brightStarImg:(UIImage *)brightImg{ return [self initWithFrame:frame stars:5 starSize:CGSizeMake(frame.size.height, frame.size.height) darkStarImg:darkImg brightStarImg:brightImg];
}

初始化一个星星评价组件,默认星星的长宽为frame的高度

- (instancetype)initWithFrame:(CGRect)frame
stars:(NSInteger)number
darkStarImg:(UIImage *)darkImg
brightStarImg:(UIImage *)brightImg{ return [self initWithFrame:frame stars:number starSize:CGSizeMake(frame.size.height, frame.size.height) darkStarImg:darkImg brightStarImg:brightImg];
}

Step3. 在当前控制器里使用代理做分数改变后的操作, 初始化星星评价组件.

使用代理做分数改变后的操作:

- (void)starsControl:(ZLStarRatingControl *)starsControl didChangeScore:(CGFloat)score{

    self.soreLabel.text = [NSString stringWithFormat:@"%.1f", score];
}

初始化星星评价组件时, 如果不需要精确只是整颗星评分,则不需要设置starsControl.allowFraction = YES;

- (ZLStarRatingControl *)starsControl{

    if (!_starsControl) {

        // 初始化一个星星评价组件
// _starsControl = [ZLStarRatingControl.alloc initWithFrame:CGRectMake(50, 100, self.view.frame.size.width - 50 * 2, 50) stars:4 starSize:CGSizeMake(50, 50) darkStarImg:[UIImage imageNamed:@"dark"] brightStarImg:[UIImage imageNamed:@"bright"]]; // 初始化一个星星评价组件,默认星星的长宽为frame的高度
// _starsControl = [ZLStarRatingControl.alloc initWithFrame:CGRectMake(50, 100, self.view.frame.size.width - 50 * 2, 50) stars:6 darkStarImg:[UIImage imageNamed:@"dark"] brightStarImg:[UIImage imageNamed:@"bright"]];
// 初始化一个星星评价组件,默认5颗星,默认星星的长宽为frame的高度
_starsControl = [ZLStarRatingControl.alloc initWithFrame:CGRectMake(50, 100, self.view.frame.size.width - 50 * 2, 50) darkStarImg:[UIImage imageNamed:@"dark"] brightStarImg:[UIImage imageNamed:@"bright"]];
_starsControl.delegate = self;
_starsControl.allowFraction = YES;
// 测试显示
_starsControl.score = 3.5f;
}
return _starsControl;
}

二、运行效果

三、压缩文件截图

界面性问题可以根据自己项目需求调整即可, 具体可参考代码, 项目能够直接运行!

注:本文著作权归作者,由demo大师发表,拒绝转载,转载需要作者授权

iOS- 非整星的评分控件(支持小数)的更多相关文章

  1. Web用户控件开发--星型评分控件

    本文中分享一个实现简单,使用方便的星型评分控件. 一:贴几张测试图片先: 二.星型评分控件的实现: RatingBar.ascx: <%@ Control Language="C#&q ...

  2. iOS 类似美团或饿了么评价中的星星评分控件

    1.做的好几个项目都用到了评分控件,可以用来展示评分,也可以用来写评分,图片和间距大小都可以定制,之前就已经简单封装了一个,现在把它分享出来,有需要的拿去用. 2.下面是展示截图:   image.p ...

  3. Android 拖动条/滑动条控件、星级评分控件

    ProgressBar有2个子控件: SeekBar   拖动条控件 RatingBar   星级评分控件 1.拖动条控件 <SeekBar android:layout_width=" ...

  4. Android星星评分控件RatingBar的使用

    在Android的开发中,有一个叫做评分控件RatingBar,我们可以使用该控件做等级划分.评分等作用,星星形状显示,也可以半星级别,我们来看一下评分控件如何使用. 布局文件中定义控件以及属性,这里 ...

  5. Android自定义评分控件:RatingStarView

    RatingStarView Android自定义的评分控件,类似ProgressBar那样的,使用星星图标(full.half.empty)作为progress标识的评分/打分控件. 效果图 图1: ...

  6. UWP开发---DIY星级评分控件

    一,需求来源 在开发韩剧TV UWP过程中,遇到了星级评分的控件问题,在安卓和html中很容易用现有的轮子实现星级评分,搜索了一下目前UWP还未有相关文章,在WPF的一篇文章中使用Photo shop ...

  7. QRowTable表格控件-支持hover整行、checked整行、指定列排序等

    目录 一.开心一刻 二.嘴一嘴 三.效果展示 四.浅谈实现 五.自定义数据源 1.data函数 2.flags函数 六.自定义视图 1.目的 2.问题分析 七.测试 八.相关文章 原文链接:QRowT ...

  8. iOS开发UI篇—手写控件,frame,center和bounds属性

    iOS开发UI基础—手写控件,frame,center和bounds属性 一.手写控件 1.手写控件的步骤 (1)使用相应的控件类创建控件对象 (2)设置该控件的各种属性 (3)添加控件到视图中 (4 ...

  9. iOS开发UI基础—手写控件,frame,center和bounds属性

    iOS开发UI基础—手写控件,frame,center和bounds属性 一.手写控件 1.手写控件的步骤 (1)使用相应的控件类创建控件对象 (2)设置该控件的各种属性 (3)添加控件到视图中 (4 ...

随机推荐

  1. Flask 学习(四)静态文件

    Flask 学习(四)静态文件 动态 web 应用也需要静态文件,一般是 CSS 和 JavaScript 文件.理想情况下你的服务器已经配置好提供静态文件的服务. 在开发过程中, Flask 也能做 ...

  2. HDU 4462 DFS

    2012 Asia Hangzhou Regional Contest 给出N*N的矩阵,所有标记为0,当中有K个点标记为1.而且能够在该位置放置一个能够覆盖曼哈顿距离为r的草人.问最少放置几个草人, ...

  3. Windows 7目录

    1. 用wubi安装的Ubuntu在重装Windows 7系统后,如何恢复(转) 2. Windows 7系统垃圾清理自写程序

  4. Objective-C:NSMutableArray类的常见操作

    可变数组NSMutableArray的内容大小是可变的,因此它的常见操作无非增删该查, 具体一些就是:创建.添加.删除.替换.插入.清空等等.. // //  main.m //  02-NSMuta ...

  5. 我所遭遇过的游戏中间件--PhysX

    我所遭遇过的游戏中间件--PhysX PhysX现在是Nvidia的物理中间件.其特点是简练且功能强大.当我最初拿到PHYSX的SDK时,就发现这个物理中间件比Havok要小很多,但该有的功能都有,甚 ...

  6. jQuery中开发插件的两种方式(附Demo)

    做web开发的基本上都会用到jQuery,jQuery插件开发两种方式:一种是类扩展的方式开发插件,jQuery添加新的全局函数(jQuery的全局函数是属于jQuery命名空间的函数),如果将jQu ...

  7. 为什么不取消注册BroadcastReceiver会导致内存泄漏

    原始问题是这样 然后扔到了很多Android开发交流群里. 接着产生了很多的见解,我感觉比较靠谱的有以下: 网友对我问题的回答 1.onDestroy被回调代不代表Activity被回收了? 官方是这 ...

  8. ios之开发者须知常见简写英文代表的含义

    <span style="white-space:pre"> </span> //NS基本 //MK地图 //CG图形绘制 //AV视音频 //UI视图 / ...

  9. Mac Finder中如何复制当前完整路径

    1.拖到命令行 2.在Finder中command+i 会弹出详细信息,然后[位置]处进行 copy 3.利用Automator,添加一个服务的快捷键. 转自:http://q.cnblogs.com ...

  10. OAuth 2.0 Authorization Framework RFC

    Internet Engineering Task Force (IETF) D. Hardt, Ed.Request for Comments: 6749 MicrosoftObsoletes: 5 ...