宽容他人,放过自己。

星星进度条创建

Posted on By anchoriteFili

星星评分定制
有响应的星星.zip
无响应的星星进度条.zip

代码分析

#import <UIKit/UIKit.h>

@class CWStarRateView; //引用星星比例view
@protocol CWStarRateViewDelegate <NSObject> //创建代理
@optional
//当比例真正改变时向外提供新的百分比
- (void)starRateView:(CWStarRateView *)starRateView scroePercentDidChange:(CGFloat)newScorePercent;
@end

@interface CWStarRateView : UIView

@property (nonatomic, assign) CGFloat scorePercent;//得分值,范围为0--1,默认为1
@property (nonatomic, assign) BOOL hasAnimation;//是否允许动画,默认为NO
@property (nonatomic, assign) BOOL allowIncompleteStar;//评分时是否允许不是整星,默认为NO

@property (nonatomic, weak) id<CWStarRateViewDelegate>delegate;

- (instancetype)initWithFrame:(CGRect)frame numberOfStars:(NSInteger)numberOfStars;

@end

#import "CWStarRateView.h"

#define FOREGROUND_STAR_IMAGE_NAME @"raise03_star_gr.png" //绿色星星
#define BACKGROUND_STAR_IMAGE_NAME @"raise03_star_whi.png" //白色星星
#define DEFALUT_STAR_NUMBER 5 //默认5个星星
#define ANIMATION_TIME_INTERVAL 0.2 //动画间隔0.2

@interface CWStarRateView ()

@property (nonatomic, strong) UIView *foregroundStarView; //前方的view
@property (nonatomic, strong) UIView *backgroundStarView; //后方的view

@property (nonatomic, assign) NSInteger numberOfStars; //星星的个数

@end

@implementation CWStarRateView

#pragma mark 重新init提醒只能用initWithFrame
- (instancetype)init {
    NSAssert(NO, @"You should never call this method in this class. Use initWithFrame: instead!");
    return nil;
}

#pragma mark 重写initWithFrame
- (instancetype)initWithFrame:(CGRect)frame {
    return [self initWithFrame:frame numberOfStars:DEFALUT_STAR_NUMBER];
}

#pragma mark 初始化编码器
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    if (self = [super initWithCoder:aDecoder]) {
//        将星星数量赋值给变量_numberOfStars
        _numberOfStars = DEFALUT_STAR_NUMBER;
        [self buildDataAndUI];
    }
    return self;
}

#pragma mark 向外提供接口
- (instancetype)initWithFrame:(CGRect)frame numberOfStars:(NSInteger)numberOfStars {
    if (self = [super initWithFrame:frame]) {
        _numberOfStars = numberOfStars;
        [self buildDataAndUI];
    }
    return self;
}

#pragma mark - Private Methods

- (void)buildDataAndUI {
    _scorePercent = 1;//百分比默认为1
    _hasAnimation = NO;//默认为NO没有动画
    _allowIncompleteStar = NO;//默认为NO默认不允许有不完整的星星
//  对前面的星星进行赋值
    self.foregroundStarView = [self createStarViewWithImage:FOREGROUND_STAR_IMAGE_NAME];
//    对后面的星星进行赋值
    self.backgroundStarView = [self createStarViewWithImage:BACKGROUND_STAR_IMAGE_NAME];
    
    [self addSubview:self.backgroundStarView];
    [self addSubview:self.foregroundStarView];
//    添加点击手势
    UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(userTapRateView:)];
    tapGesture.numberOfTapsRequired = 1;
    [self addGestureRecognizer:tapGesture];
}

#pragma mark 点击手势触发事件
- (void)userTapRateView:(UITapGestureRecognizer *)gesture {
    CGPoint tapPoint = [gesture locationInView:self]; //获取手势点击的点
    CGFloat offset = tapPoint.x; //获取手势偏移量
    CGFloat realStarScore = offset / (self.bounds.size.width / self.numberOfStars); //获取这个偏移量占星星宽度的比例
    CGFloat starScore = self.allowIncompleteStar ? realStarScore : ceilf(realStarScore); //如果允许使用不完整星星,则用realStarScore数据,否则用ceilf(realStarScore)
    self.scorePercent = starScore / self.numberOfStars; //星星百分比 = 偏移量 / 星星数
}

#pragma mark 开始创建星星图片
- (UIView *)createStarViewWithImage:(NSString *)imageName {
    UIView *view = [[UIView alloc] initWithFrame:self.bounds];
//    当返回yes时对边界进行修剪,返回no时不对边界修剪
    view.clipsToBounds = YES;
    view.backgroundColor = [UIColor clearColor];
//    根据星星的个数用for循环创建星星
    for (NSInteger i = 0; i < self.numberOfStars; i ++)
    {
        UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:imageName]];
        imageView.frame = CGRectMake(i * self.bounds.size.width / self.numberOfStars, 0, self.bounds.size.width / self.numberOfStars, self.bounds.size.height);
//        内部图片适应imageView比例
        imageView.contentMode = UIViewContentModeScaleAspectFit;
        [view addSubview:imageView];
    }
    return view;
}

#pragma mark 
- (void)layoutSubviews {
    [super layoutSubviews];
    __weak CWStarRateView *weakSelf = self; //方法中弱引用自己
    CGFloat animationTimeInterval = self.hasAnimation ? ANIMATION_TIME_INTERVAL : 0; //如果设置了动画间隔时间久用,如果没有设置就默认为0
    [UIView animateWithDuration:animationTimeInterval animations:^{
//        为前面的星星view添加动画效果
       weakSelf.foregroundStarView.frame = CGRectMake(0, 0, weakSelf.bounds.size.width * weakSelf.scorePercent, weakSelf.bounds.size.height);
    }];
}

#pragma mark

- (void)setScorePercent:(CGFloat)scroePercent {
    /**
     如果传过来的百分比与变量数值相等,则直接返回,
     如果传过来的百分比<0,则直接对变量_scorePercent赋0
     如果传过来的百分比>1,则对变量赋1
     如果传过来的百分比在0和1之间,则对变量进行赋值
     */
    if (_scorePercent == scroePercent) {
        return;
    }
    
    if (scroePercent < 0) {
        _scorePercent = 0;
    } else if (scroePercent > 1) {
        _scorePercent = 1;
    } else {
        _scorePercent = scroePercent;
    }
    
#pragma mark 当代理响应了代理方法,则将参数scroePercent通过代理方法直接传过去
    if ([self.delegate respondsToSelector:@selector(starRateView:scroePercentDidChange:)]) {
        [self.delegate starRateView:self scroePercentDidChange:scroePercent];
    }
    
    [self setNeedsLayout];
}

@end