iOS 一个可高度自定义化的评分控件、打分、打星

  在近期的开发计划中,我们的开发计划有关于商品打分的功能需求列了进来,我就提前看了下,网上的这种demo实在太多了,但是都不是很符合我们的需求,索性自己写一个得了,那就开始动手。先看一下最终效果(最后有demo哦):


QQ20181115-174009-HD.gif

  那有兴趣的同学继续往下看。
  先理一下实现思路,我的实现思路是利用CALayer的maskLayer来实现,就跟一般的进度条实现思路差不多,不过这次要用带星的view来做mask。具体实现看下面。
  首先写maskLayer的view 也就是带星星的view:

#import <UIKit/UIKit.h>
typedef NS_ENUM(NSInteger, CYXRatingStarStyle) {
    RatingStarStyleFull = 0, //满星打分
    RatingStarStyleHalf = 1, //可半星打分
};
NS_ASSUME_NONNULL_BEGIN

@interface CYXRatingStarMaskView : UIView

/*星星个数*/
-(instancetype)initWithStarNum:(NSInteger)starNum;
/**
 初始化方法
 
 @param starNum 星星个数
 @param space 星星直接的间距 默认15
 @return self
 */
-(instancetype)initWithStarNum:(NSInteger)starNum andSpace:(CGFloat)space;
/*更新布局 在设置frame之后调用*/
-(void)updateViewConstrains;
/*触摸*/
-(void)touchesWithPoint:(CGPoint)touchPoint;
/*打分风格*/
@property (nonatomic,assign) CYXRatingStarStyle ratingStarStyle;
/*总分*/
@property (nonatomic,assign) NSInteger fullScore;
/*点击 调用block  transformPoint转换过的点  score得分*/
@property (nonatomic,strong) void(^touchBlock)(CGPoint transformPoint,NSInteger score);

@end

相关实现(多的我就不再啰嗦了注释里面都有):

#import "CYXRatingStarMaskView.h"
@interface CYXRatingStarMaskView ()
/*星星个数 最少两个*/
@property (nonatomic,assign) NSInteger starNum;
/*星星间距 默认15*/
@property (nonatomic,assign) CGFloat space;

@property (nonatomic,strong) NSMutableArray *itemList;
@end
@implementation CYXRatingStarMaskView
-(instancetype)initWithStarNum:(NSInteger)starNum{
    return [self initWithStarNum:starNum andSpace:15];
}

-(instancetype)initWithStarNum:(NSInteger)starNum andSpace:(CGFloat)space{
    if (self = [super init]) {
        self.starNum = starNum;
        self.space = space;
        [self createItems];
    }
    return self;
}
-(void)createItems{
    self.itemList = [NSMutableArray new];
    for (int i =0; i<self.starNum; i++) {
        UIImageView * item = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"starUnselected"]];
        [self.itemList addObject:item];
        [self addSubview:item];
    }
}
-(void)updateViewConstrains{
    if (self.starNum>1) {
        CGFloat itemWidth = (self.frame.size.width-(self.starNum -1)*self.space)/self.starNum;
        if (itemWidth>0) {
            CGFloat x = 0;
            for (UIImageView * item in self.itemList) {
                item.frame = CGRectMake(x, 0, itemWidth, self.frame.size.height);
                x+=itemWidth;
                x+=self.space;
            }
        }
    }
}
-(void)touchesWithPoint:(CGPoint)touchPoint{
    [self transformPointWithTouchPoint:touchPoint];
}

/*将触点转化成填充星星的点*/
-(CGPoint)transformPointWithTouchPoint:(CGPoint)touchPoint{
    /*满星平均分*/
    NSInteger average = self.fullScore/[self.itemList count];
    /*触摸点x*/
    CGFloat x = touchPoint.x;
    /*得分*/
    CGFloat score = 0;
    /*倒序遍历*/
    for (NSInteger i = [self.itemList count]-1; i>=0; i--) {
        UIImageView * item = self.itemList[i];
        if (x>item.frame.origin.x) {
            switch (self.ratingStarStyle) {
                case RatingStarStyleFull:{//满星
                    score = (i+1)*average;
                    x = item.frame.origin.x+item.frame.size.width;
                }
                    break;
                case RatingStarStyleHalf:{//支持半星
                    if (x>(item.frame.origin.x+item.frame.size.width/2)) {//超过半星
                        score = (i+1)*average;
                        x = item.frame.origin.x+item.frame.size.width;
                    }else{
                        score = (i+1)*average - (average/2);
                        x = item.frame.origin.x+item.frame.size.width/2;
                    }
                    
                }
                    break;
                default:
                    break;
            }
            break;
        }
    }
    CGPoint transformPoint = CGPointMake(x, touchPoint.y);
    if (self.touchBlock) {
        self.touchBlock(transformPoint, score);
    }
    return CGPointZero;
}

然后创建用于打分的View来利用这个maskview实现:

#import <UIKit/UIKit.h>
#import "CYXRatingStarMaskView.h"
NS_ASSUME_NONNULL_BEGIN
@interface CYXRatingStarView : UIView

/**
 初始化方法

 @param frame frame
 @param ratingStarStyle 打分风格
 @param fullScore 满分
 @return self
 */
-(instancetype)initWithFrame:(CGRect)frame
          andRatingStarStyle:(CYXRatingStarStyle)ratingStarStyle
                andFullScore:(NSInteger)fullScore;
/**
 初始化layer 在完成frame赋值后调用一下
 */
-(void)initLayers;
/*选择分数*/
@property (nonatomic,strong) void (^selectScore)(NSInteger score);
@end

相关实现(没啥难点都有注释自己看吧):

#import "CYXRatingStarView.h"

@interface CYXRatingStarView()
/*背景红色*/
@property (nonatomic,strong) CAShapeLayer *backColorLayer;
@property (nonatomic,strong) CYXRatingStarMaskView *maskView;
@end
@implementation CYXRatingStarView

-(instancetype)initWithFrame:(CGRect)frame
          andRatingStarStyle:(CYXRatingStarStyle)ratingStarStyle
                andFullScore:(NSInteger)fullScore{
    if (self = [super initWithFrame:frame]) {
        self.backgroundColor = [UIColor whiteColor];
        self.maskView.fullScore = fullScore;
        self.maskView.ratingStarStyle = ratingStarStyle;
        [self.layer addSublayer:self.backColorLayer];
        
    }
    return self;
}
-(void)initLayers{
    self.maskView.frame = self.bounds;
    [self.maskView updateViewConstrains];
    self.backColorLayer.frame = self.bounds;
    UIBezierPath *path = [UIBezierPath bezierPath];
    [path moveToPoint:CGPointMake(0, self.frame.size.height/2)];
    [path addLineToPoint:CGPointMake(self.frame.size.width, self.frame.size.height/2)];
    self.backColorLayer.path = path.CGPath;
    self.backColorLayer.lineWidth = self.frame.size.height;
    self.backColorLayer.mask = self.maskView.layer;
    self.backColorLayer.strokeEnd = 0;
}
/*设置选择的星星*/
-(void)setStrokeWithTransformPoint:(CGPoint)transformPoint{
    CGPoint newPoint = [self convertPoint:transformPoint fromView:self.maskView];
    NSLog(@"%f",newPoint.x);
    self.backColorLayer.strokeEnd = newPoint.x/self.frame.size.width;
}
/*一根或者多根手指开始触摸view,系统会自动调用view的下面方法*/
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    //获取touch
    UITouch * touch = [touches anyObject];
    //获取当前点
    CGPoint touchPoint = [touch locationInView:self];
    CGPoint newPoint = [self convertPoint:touchPoint toView:self.maskView];
    [self.maskView touchesWithPoint:newPoint];
}
#pragma mark ---G
-(CAShapeLayer*)backColorLayer{
    if(!_backColorLayer){
        _backColorLayer = [[CAShapeLayer alloc] init];
        _backColorLayer.backgroundColor = [UIColor grayColor].CGColor;//在这里是未填充的颜色
        _backColorLayer.fillColor = [UIColor clearColor].CGColor; // 填充色为透明(不设置为黑色)
        //_backColorLayer.lineCap = kCALineCapSquare; // 设置线为圆角
        _backColorLayer.strokeColor = [UIColor redColor].CGColor; // 路径颜色颜色(填充颜色)
    }
    return _backColorLayer;
}
-(CYXRatingStarMaskView*)maskView{
    if(!_maskView){
        __weak __typeof(&*self)weakSelf = self;
        _maskView = [[CYXRatingStarMaskView alloc] initWithStarNum:5];
        _maskView.touchBlock = ^(CGPoint transformPoint, NSInteger score) {
            NSLog(@"%@",NSStringFromCGPoint(transformPoint));
            NSLog(@"%ld",(long)score);
            [weakSelf setStrokeWithTransformPoint:transformPoint];
            if (weakSelf.selectScore) {
                weakSelf.selectScore(score);
            }
        };
    }
    return _maskView;
}

  总体写下来思路还是很明确的,而且实现过程当中并没有什么疑难杂症,有什么问题欢迎讨论。
demo地址:https://github.com/SionChen/CYXRatingStarViewDemo

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,928评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,192评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,468评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,186评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,295评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,374评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,403评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,186评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,610评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,906评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,075评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,755评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,393评论 3 320
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,079评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,313评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,934评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,963评论 2 351

推荐阅读更多精彩内容