ios开发——滑动星型评分控件的实现

在App Store或者其他一些应用中我们可以通过点击或滑动星星来给应用评分,效果图如下


现在我们来实现这一功能。


首先我们需要准备两张图片作为素材,一个是灰色背景星星,另一个是黄色星星表示评分。

 (亮色星星)        (暗色星星)

实际操作中可以用自己需要的图片替代。


接着,我们建立自己的类继承View,来实现这个评分视图,这里假设起名为CYZStarRageView。现在来看一下头文件


@class CYZStarRateView;

@protocol CYZStarRateViewDelegate <NSObject>

/**
 *  通知代理改变评分到某一特定的值
 *
 *  @param starRateView 指当前评分view
 *  @param percentage   新的评分值
 */
- (void)starRateView:(CYZStarRateView *)starRateView didChangedScorePercentageTo:(CGFloat)percentage;

@end

@interface CYZStarRateView : UIView

/**
 *  代理
 */
@property (weak, nonatomic) id<CYZStarRateViewDelegate> delegate;
/**
 *  是否使用动画,默认为NO
 */
@property (assign, nonatomic) BOOL shouldUseAnimation;
/**
 *  是否允许非整型评分,默认为NO
 */
@property (assign, nonatomic) BOOL allowIncompleteStar;
/**
 *  是否允许用户手指操作评分,默认为YES
 */
@property (assign, nonatomic) BOOL allowUserInteraction;
/**
 *  当前评分值,范围0---1,表示的是黄色星星占的百分比,默认为1
 */
@property (assign, nonatomic) CGFloat percentage;

/**
 *  初始化方法,需传入评分星星的总数
 *
 *  @param frame 该starView的大小与位置
 *  @param count 评分星星的数量
 *
 *  @return 实例变量
 */
- (id)initWithFrame:(CGRect)frame starCount:(NSInteger)count;
/**
 *  设置当前评分为某一值,是否使用动画取决于shouldUseAnimation属性的取值
 *
 *  @param score 新的评分值
 */
- (void)setScore:(CGFloat)score;

首先写一个协议,当评分更改后会触发该协议方法。其次,对于这个类,设立一些属性来控制相应的功能。例如:是否允许非整型评分,是否允许用户交互,是否设立动画等等。同时需要一个初始化方法,在这里传入星星的数量。应用时可以通过直接设置percentage属性或者通过调用setScore方法来设置,实质上这两个是一样的。


关键代码:

一、初始化方法。

在初始化方法中,为私有变量starCount赋值,计算出每个星星的宽度以便之后初始化子视图用。接着对默认值、子视图、手势进行初始化。为了让该方法保持“整洁”,将后面的一系列操作封装到一个私有方法- (void)initStarView 中。

- (id)initWithFrame:(CGRect)frame starCount:(NSInteger)count {
    self = [super initWithFrame:frame];
    if (self) {
        self.starCount = count;
        self.starWidth = (CGFloat)self.frame.size.width / self.starCount;
        
        [self initStarView];
    }
    return self;
}

initStarView方法:

- (void)initStarView {
    //默认值
    self.percentage = 1.0f;
    self.shouldUseAnimation = NO;
    self.allowIncompleteStar = NO;
    self.allowUserInteraction = YES;
    
    //星星视图
    self.lightStarView = [self starViewWithImageName:LIGHT_STAR_IMAGE_NAME];
    self.grayStarView = [self starViewWithImageName:DARK_STAR_IMAGE_NAME];
    
    [self addSubview:self.grayStarView];
    [self addSubview:self.lightStarView];
    
    //此处用pan手势,达到用户可以滑动手指评分的效果
    self.pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
    [self addGestureRecognizer:self.pan];
}

二、添加子视图方法

之前在初始化方法中已经获得了评分星星的总数量,接着将相应数量的星星添加到该视图中即可。

- (UIView *)starViewWithImageName:(NSString *)imageName {
    UIView *view = [[UIView alloc] initWithFrame:self.bounds];
    
    view.clipsToBounds = YES;
    view.backgroundColor = [UIColor clearColor];
    //添加星星
    for (int i = 0; i < self.starCount; i++) {
        UIImageView *iv = [[UIImageView alloc] initWithFrame:CGRectMake(i * self.starWidth, 0, self.starWidth, self.bounds.size.height)];
        iv.image = [UIImage imageNamed:imageName];
        iv.contentMode = UIViewContentModeScaleAspectFit;
        [view addSubview:iv];
    }
    
    return view;
}

设置view的clipsToBounds属性来达到记下来评分的效果。注意到之前的初始化方法中得到了gray视图和light视图后,添加子视图的顺序不能变。我们要做的就是根据评分让亮星星覆盖暗星星.


三、手势的相应方法

这里算是比较核心的代码了,为了实现手指滑动评分,我们选择的方法是添加pan手势(如果不要这个功能,简单的tap手势即可)。所以在这里我们根据手势的状态进行相应的计算和操作

- (void)handlePan:(UIPanGestureRecognizer *)recognizer {
    static CGFloat startX = 0;
    CGFloat starScorePercentage = 0;
    
    if (recognizer.state == UIGestureRecognizerStateBegan) {
        startX = [recognizer locationInView:self].x;
        starScorePercentage = startX / self.starWidth;
        
    } else if (recognizer.state == UIGestureRecognizerStateChanged) {
        CGFloat location = [recognizer translationInView:self].x + startX;
        starScorePercentage = location / self.starWidth;
    } else {
        return;
    }
    
    CGFloat realScore = self.allowIncompleteStar ? starScorePercentage : ceilf(starScorePercentage);
    self.percentage = realScore / self.starCount;
}

思路是,记录起点,记录偏移量,计算偏移量是单位星星宽度的多少倍,然后根据是否允许非整形评分来计算实际倍数,用该值去除以总数即可得到范围为0-1的评分。给percentage赋值即可,接下来可以看到,我们在percentage的setter中将进行额外的操作。


四、setter

直接贴上代码:

- (void)setPercentage:(CGFloat)percentage {
    
    if (percentage >= 1) {
        _percentage = 1;
    } else if (percentage < 0) {
        _percentage = 0;
    } else {
        _percentage = percentage;
    }
    
    [self setNeedsLayout];
    
    if ([self.delegate respondsToSelector:@selector(starRateView:didChangedScorePercentageTo:)]) {
        [self.delegate starRateView:self didChangedScorePercentageTo:_percentage];
    }
}
这里主要是对数值的合理性检验,调用代理,以及通知view进行重新布局。在布局方法中我们真正实现评分的效果


五、layoutSubview方法


- (void)layoutSubviews {
    [super layoutSubviews];
    
    __weak CYZStarRateView *weakSelf = self;
    
    CGFloat duration = self.shouldUseAnimation ? DEFAULT_DURATION : 0.0f;
    [UIView animateWithDuration:duration animations:^{
        weakSelf.lightStarView.frame = CGRectMake(0, 0, weakSelf.percentage * weakSelf.bounds.size.width, weakSelf.bounds.size.height);
    }];
}

通过改变lightStarView的frame来改变亮星星的显示个数和范围,露出下面的灰色星星。这样就达到我们想要的效果了。



郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。