在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;
关键代码:
一、初始化方法。
在初始化方法中,为私有变量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;
}- (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;
}
三、手势的相应方法
这里算是比较核心的代码了,为了实现手指滑动评分,我们选择的方法是添加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;
}
四、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);
}];
}
原文:http://blog.csdn.net/u013604612/article/details/41707617