iOS-实现星级评分

作者: 码渣 | 来源:发表于2018-08-30 18:00 被阅读1次

我们知道,很多app都有星星评分的功能,特别是商城app,需要你对商品描述、发货速度、服务态度等进行打分。
我开发的app正好也需要这个功能,所以自己封装了一个,使用起来也是很简单,满足大部分需要,可全星、可半星、可不完整星、可点击、可滑动、可设置星星数量、大小、间隔等,一切根据你的需要来。

首先看.h头文件

typedef NS_ENUM(NSInteger, GBStarRateViewStyle){
    
    GBStarRateViewStyleWholeStar = 0,//全星评分
    GBStarRateViewStyleHalfStar = 1, //可半星评分
    GBStarRateViewStyleIncompleteStar = 2,//不完整星评分
};
@class GBStarRateView;
@protocol GBStarRateViewDelegate <NSObject>
@optional
- (void)starRateView:(GBStarRateView *)starRateView didSelecteStarAtStarRate:(CGFloat)starRate;
@end

@interface GBStarRateView : UIView

@property (nonatomic, assign) GBStarRateViewStyle style; //星星评分样式
@property (nonatomic, weak) id <GBStarRateViewDelegate> delegate; //代理
@property (nonatomic, assign) NSInteger numberOfStars; //星星数量 默认为5
@property (nonatomic, assign) CGFloat currentStarRate; //当前打分 默认为0.0
@property (nonatomic, assign) CGFloat spacingBetweenStars; //星星间隔 默认为10
@property (nonatomic, assign) CGSize starSize; //星星尺寸 默认 {24,24}
@property (nonatomic, strong) UIImage *starImage; //未选中star image 有默认图片
@property (nonatomic, strong) UIImage *currentStarImage; //选中star image 有默认图片
@property (nonatomic, assign) BOOL isAnimation; //是否动画 默认YES
@property (nonatomic, assign) BOOL allowSlideScore; //是否滑动打分 默认NO 适用于不完整星星打分
@property (nonatomic, assign) BOOL allowClickScore; //是否点击打分 默认YES
@property (nonatomic, copy) void(^didSelectStarBlock)(GBStarRateView *starRateView, CGFloat starRate);//点击或滑动打分的回调
@end

然后再看一下.m的实现

#import "GBStarRateView.h"

static CGFloat const kAnimatinDuration = 0.3;

@interface GBStarRateView ()

@property (nonatomic, strong) NSMutableArray <UIView *> *starsContentViews;

@end


@implementation GBStarRateView

@synthesize starSize = _starSize;

- (id)init {
    
    if (self = [super init]) {
        
        [self config];
    }
    return self;
}

- (id)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        
        [self config];
    }
    return self;
}

- (id)initWithCoder:(NSCoder *)aDecoder {
    
    if (self = [super initWithCoder:aDecoder]) {
        
        [self config];
    }
    return self;
}
#pragma mark - 基本配置
- (void)config {
    
    self.style = GBStarRateViewStyleWholeStar;
    self.numberOfStars = 5;
    self.spacingBetweenStars = 10;
    _starSize = CGSizeMake(24, 24);
    self.currentStarImage = [UIImage imageNamed:@"ic_star_selected"];
    self.starImage = [UIImage imageNamed:@"ic_star_default"];
    self.isAnimation = YES;
    self.allowClickScore = YES;
    self.allowSlideScore = NO;
}

#pragma mark - 创建视图
- (void)resetStarsContentView {
    
    for (UIView *starContentView in self.starsContentViews) {
        [starContentView removeFromSuperview];
    }
    [self.starsContentViews removeAllObjects];
    
    [self createStarsContentView:self.starImage starRate:_numberOfStars];
    [self createStarsContentView:self.currentStarImage starRate:_currentStarRate];
}

- (void)createStarsContentView:(UIImage *)starImage starRate:(CGFloat)starRate {
    
    if (self.numberOfStars == 0) {
        return;
    }
    CGRect frame = [self frameForStarsContentViewAtCurrentStarRate:starRate];
    UIView *starsContentView = [[UIView alloc] initWithFrame:frame];
    starsContentView.clipsToBounds = YES;//必须要设置,不设试试效果
    [self addSubview:starsContentView];
    
    for (int i = 0; i < self.numberOfStars; i++) {
        
        UIImageView *imageView = [[UIImageView alloc] initWithImage:starImage];
        imageView.frame = CGRectMake((self.starSize.width + self.spacingBetweenStars) * i, 0, self.starSize.width, self.starSize.height);
        imageView.contentMode = UIViewContentModeScaleAspectFit;
        [starsContentView addSubview:imageView];
    }
    
    [self.starsContentViews addObject:starsContentView];
}
#pragma mark - StarsContentView frame
- (CGRect)frameForStarsContentViewAtCurrentStarRate:(CGFloat)currentStarRate {
    
    NSInteger index = (NSInteger)floor(currentStarRate);
    CGFloat w = (self.starSize.width + self.spacingBetweenStars) * index + (currentStarRate - index) * self.starSize.width;
    CGFloat x = (CGRectGetWidth(self.bounds) - [self sizeForNumberOfStar:self.numberOfStars].width) * 0.5;
    CGFloat y = (CGRectGetHeight(self.bounds) - [self sizeForNumberOfStar:self.numberOfStars].height) * 0.5;
    CGFloat h = self.starSize.height;
    return CGRectMake(x, y, w, h);
}

#pragma mark - setter
- (void)setNumberOfStars:(NSInteger)numberOfStars {
    
    if (_numberOfStars != numberOfStars) {
        _numberOfStars = numberOfStars;
        [self resetStarsContentView];
    }
}

- (void)setSpacingBetweenStars:(CGFloat)spacingBetweenStars {
    
    if (_spacingBetweenStars != spacingBetweenStars) {
        _spacingBetweenStars = spacingBetweenStars;
        [self resetStarsContentView];
    }
}

- (void)setStarImage:(UIImage *)starImage {
    
    if (_starImage != starImage) {
        _starImage = starImage;
        [self resetStarsContentView];
    }
}
- (void)setCurrentStarImage:(UIImage *)currentStarImage {
    
    if (_currentStarImage != currentStarImage) {
        _currentStarImage = currentStarImage;
        [self resetStarsContentView];
    }
}

- (void)setStarSize:(CGSize)starSize {
    
    if (!CGSizeEqualToSize(_starSize, starSize)) {
        _starSize = starSize;
        [self resetStarsContentView];
    }
}

- (CGSize)starSize {

    if (CGSizeEqualToSize(_starSize, CGSizeZero)) {

        _starSize = self.starImage.size;
    }
    return _starSize;
}

- (void)setCurrentStarRate:(CGFloat)currentStarRate {
    
    if (self.starsContentViews.count == 0 || _currentStarRate == currentStarRate) {
        return;
    }
    if (currentStarRate  < 0) {
        return;
    } else if (currentStarRate > self.numberOfStars) {
        
        _currentStarRate = self.numberOfStars;
    } else {
        _currentStarRate = currentStarRate;
    }

    UIView *starsContentView = self.starsContentViews[1];
    [UIView animateWithDuration:_isAnimation ? kAnimatinDuration : 0.0 animations:^{
        starsContentView.frame = [self frameForStarsContentViewAtCurrentStarRate:currentStarRate];
    }];
    if (self.didSelectStarBlock) {
        self.didSelectStarBlock(self, currentStarRate);
    }
    if ([self.delegate respondsToSelector:@selector(starRateView:didSelecteStarAtStarRate:)]) {
        [self.delegate starRateView:self didSelecteStarAtStarRate:currentStarRate];
    }
}

#pragma mark - event
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {

    if (_allowClickScore) {
        UITouch *touch = [touches anyObject];
        UIView *view = touch.view;
        if (view != self) {
            CGPoint point = [touch locationInView:view];
            [self setupScoreWithOffsetX:point.x];
        }
    }
}

- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    if (_allowSlideScore) {
        UITouch *touch = [touches anyObject];
        UIView *view = touch.view;
        if (view != self && [self.starsContentViews containsObject:view]) {
            CGPoint point = [touch locationInView:view];
            [self setupScoreWithOffsetX:point.x];
        }
    }
}

#pragma mark - 根据offsetx计算分数
- (void)setupScoreWithOffsetX:(CGFloat)offsetX {
    
    NSInteger index = offsetX / (self.starSize.width + self.spacingBetweenStars);
    CGFloat mathOffsetX =  (index + 1) * self.starSize.width + index * self.spacingBetweenStars;
    CGFloat score = (offsetX - index * self.spacingBetweenStars)/(self.starSize.width);
    if (offsetX > mathOffsetX) {
        score = index + 1;
    }
    NSLog(@"offsetX=%f,index=%ld, score=%f", offsetX, index, score);
    self.currentStarRate = [self currentStarRateWithScore:score];
}

- (CGFloat)currentStarRateWithScore:(CGFloat)score {
    
    switch (self.style) {
        case GBStarRateViewStyleWholeStar:
            score = ceil(score);
            break;
        case GBStarRateViewStyleHalfStar:
            score = round(score) > score ? round(score) : (ceil(score)-0.5);
            break;
        case GBStarRateViewStyleIncompleteStar:
            score = score;
            break;
    }
    return score;
}


- (CGSize)sizeForNumberOfStar:(NSInteger)starCount {
    
    CGFloat w = (self.starSize.width + self.spacingBetweenStars)*starCount - self.spacingBetweenStars;
    return CGSizeMake(w, self.starSize.height);
}


- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
    
    //实现在星星范围内也能响应 即使父视图高度少于星星高度
    if (point.y <= (self.starImage.size.height*0.5) && point.y >= - (self.starImage.size.height*0.5 - self.bounds.size.height*0.5)) {
        return YES;
    }
    return [super pointInside:point withEvent:event];
}


#pragma mark - getter
- (NSMutableArray<UIView *> *)starsContentViews {
    
    if (!_starsContentViews) {
        _starsContentViews = [NSMutableArray array];
    }
    return _starsContentViews;
}
@end

最后就是下载链接啦--- 点击下载demo

相关文章

  • iOS-实现星级评分

    我们知道,很多app都有星星评分的功能,特别是商城app,需要你对商品描述、发货速度、服务态度等进行打分。我开发的...

  • 【iOS】实现星级评分

    许多App都会有评价功能,这个时候或许会需要实现星级评分,下面我们来简单的实现一个星级评分功能。 具体实现 导入资...

  • 星级评分

    一、背景 许多App都会有评价功能,这个时候或许会需要实现星级评分,下面我们来简单的实现一个星级评分功能。 二、简...

  • JS实现星级评分

    朋友给我一份星级评价的html代码,说不能实现多行星级评价,让我帮忙看一下。 帮朋友修改的,怕到时候自己也用到了,...

  • 星星评分控件TQStarRatingView揭秘!(一)

    今天为大家带来一个iOS星级评分控件的实现,欢迎拍砖。 在github上搜索iOS星级评分控件,可以看到TQSta...

  • 星星评分

    星星评分 我们很多时候 为了 UI界面的 简便 美观 ,时常用到 星级评分 1.实现思路 2.代码实现 .h 文件...

  • 星级评分篇一 最初实现

    0x01 前言   星级评分的实现网上例子诸多,然而写这篇文章是为了,在学习完视频和复现效果后的记录和反思。视频中...

  • jQuery实现简单星级评分效果

  • iOS星级评分效果实现

    在开发中我们经常需要实现评价星级的功能,这里分享一个评星效果的实现过程。 效果: Demo地址:https://g...

  • 星级评分条,不用ratingbar

    星级评分条,不用ratingbar# 使用seekbar 说起星级评分,最先想到的就是ratingbar...但是...

网友评论

    本文标题:iOS-实现星级评分

    本文链接:https://www.haomeiwen.com/subject/ssldwftx.html