Wednesday, December 26, 2012

AHSStarRating for iOS


AHSStarRating for iOS

iOS star Rating with a different logic :

Hi friends in this blog post i'm going to explain you that how to create a rating control(say, 5 star rating) by the help of touch events. Below is a snapshot of AHSStarRating,


From this logic you can able to rate even fractions & no need of n number of star images just a single image as below is sufficient.


pros:
1. Since we need only 1 image, bundle size will be reduced.
2. You can rate (& get) even fraction values.


Coming to the logic i have a rating control class called AHSRatingView which is a subclass of UIView, this rating control class you can add to any of viewController class like below,

- (void)viewDidLoad
{
    [super viewDidLoad];
    UIImage *ratingImg = [UIImage imageNamed:@"starRatingImg"];
    ahsRatingVw = [[AHSRatingView alloc] initWithFrame:CGRectMake(10.0, 100.0, ratingImg.size.width, ratingImg.size.height) andStarColor:[UIColor redColor]];
    ahsRatingVw.delegate = self;
    [self.view addSubview:ahsRatingVw];
}

their is a custom init method called "initWithFrame: andStarColor:" in AHSRatingView which you need to call so as to show rating control, initWithFrame: accepts a cgrect parameter where we will pass a CGRect for rate control its a good idea to get your rating image dimensions & set those width & height values, & andStarColor: accepts a UIColor type of parameter where yo can pass any of UIColor you wish, this color will be your rating star color.
After this just set the delegate "AHSRating"  in your viewController class file as below so as to get the user rating value from the AHSRatingView(rating control),

@interface ViewController : UIViewController<AHSRating>

@property (retain, nonatomic) IBOutlet UILabel *ahsRatingCountLbl; //property to get user rated value

and in your viewController implementation file if you wanna want to display user rated value just define AHSRatingView delegate method as below or you can make use of this count for further processing as per your application requirement, below is the snippet where as of now we are just updating user rated value to a UILabel.

#pragma mark - AHSRating Delegate
- (void)getRatingCount:(float)rateCount{
    ahsRatingCountLbl.text = [NSString stringWithFormat:@"your rating count is %f",rateCount];
}

thats it in your calling class.

Now coming to AHSRatingView, this is the main rating control, below is the snapshot of AHSRatingView interface file which you can refer,

AHSRatingView.h
#import <UIKit/UIKit.h>

@protocol AHSRating <NSObject>
@optional
    - (void)getRatingCount:(float)rateCount;
@end

@interface AHSRatingView : UIView
{
    UIImageView *ratingImgVw;
    CGPoint startPoint;
    CGPoint endPoint;
    UIView *fillColorView;
    CGFloat ratingViewFrameWidth;
    CGFloat singleStarRect;
    CGFloat currentStarRating;
    UIColor *ratingColor;
}
@property(nonatomic,retain) id<AHSRating> delegate;
- (id)initWithFrame:(CGRect)frame andStarColor:(UIColor *)color;
@end

here in interface file we are having instance variables which are required first is object ratingImgVw of type UIImageView so as to hold your rating image, startPoint & endPoint is of type CGPoint so as to capture user touch points, fillColorView is of UIView type so as to fill (rate) stars, ratingViewFrameWidth, singleStarRect, currentStarRating are iVars for core user rating calculation(logic), ratingColor is of type UIColor which will effect to stars. 
and on top there is a protocol definition which is defined  just to update calling class with user rated value whenever he rates.

Below is the snapshot of AHSRatingView implementation file which you can refer,

AHSRatingView.m
#import "AHSRatingView.h"
#define kNumberOfStars 5
#define kdefaultFillColor [UIColor blueColor]

@interface AHSRatingView(Private)
- (void)rating;
@end

@implementation AHSRatingView
@synthesize delegate;

- (id)initWithFrame:(CGRect)frame andStarColor:(UIColor *)color{
    self = [super initWithFrame:frame];
    if (self) {
        //initilization code
        self.clipsToBounds = YES;
        
        ratingColor = color;
        if (!ratingColor) {
            ratingColor = kdefaultFillColor;
        }
        ratingImgVw=[[UIImageView alloc] initWithFrame:CGRectMake(0, 0, frame.size.width, frame.size.height)];
        ratingImgVw.contentMode=UIViewContentModeScaleToFill;
        ratingImgVw.image=[UIImage imageNamed:@"starRatingImg"];
        ratingImgVw.clipsToBounds = YES;
        [self addSubview:ratingImgVw];
        
        ratingViewFrameWidth = self.frame.size.width;
    }
    return self;
}

#pragma mark - Touch Events Methods
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [touches anyObject];
    CGPoint point = [touch locationInView:self];
    startPoint = point;
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [touches anyObject];
    CGPoint point = [touch locationInView:self];
    endPoint = point;
    [self rating];
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
    UITouch *touch = [touches anyObject];
    CGPoint point = [touch locationInView:self];
    endPoint = point;
    singleStarRect = ratingViewFrameWidth/kNumberOfStars;//get each single star rect(width)
    currentStarRating = endPoint.x/singleStarRect;//get rating value
    
    /* handling exception */
    if (currentStarRating > 5.0) {
        currentStarRating = 5.0;
    }else if(currentStarRating < 0.0){
        currentStarRating = 0.0;
    }
    
    if (delegate) {
        if ([delegate respondsToSelector:@selector(getRatingCount:)]) {
            [delegate getRatingCount:currentStarRating];
        }
    }
    [self rating];
}

/* cancel in the sense on touch event if a call comes then this method ll trigger */
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event{
    /* handle ur exception code */
    NSLog(@"Touches Cancelled");
}

#pragma mark - Rating Method
- (void)rating{
    [UIView animateWithDuration:0.50
                          delay:0.0
                        options: UIViewAnimationCurveLinear
                     animations:^{
                         if (fillColorView) {
                             [fillColorView removeFromSuperview];
                             fillColorView = nil;
                         }
                         fillColorView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, endPoint.x, self.frame.size.height)];
                         fillColorView.backgroundColor = ratingColor;
                         [self addSubview:fillColorView];
                         [self sendSubviewToBack:fillColorView];                         
                     } 
                     completion:^(BOOL finished){
                         // NSLog(@"Done!");
                     }];
    
}
@end

here in implementation file mainly we are dealing with touch events initially we will initialize our rating view with a rating image frame & a specified color, & we are getting the entire view frame width in ratingViewFrameWidth iVar. Then we are capturing touch event by touchesBegan:, touchesMoved: & touchesEnded: events. where in touchesBegan: we are capturing the starting point, touchesMoved: we are capturing the user swipe movement (points) & parallel updating star rating by calling "rating" method, & in touchesEnded: we are capturing the end Point & in parallel  calculating the rate count & informing all delegates, & updating rate by calling "rating" method.

rating instance method is actually updating ui by rating with specified color for stars.

and tats all, hope you enjoyed the post any comments either pros or cons or suggestions or whatever is acceptable from my side.
Thank you,,,:-)