Showing posts with label indicator. Show all posts
Showing posts with label indicator. Show all posts

Monday, 26 June 2017

Circular Progress Bar Indicator

iOS don't provide pre-build 'Circular Progress View/Bar' like UIProgressBar.
Implementing a 'Circular Progress View' is simple in iOS. Here piece of code for implement 'Circular Progress View'.

1) Create a Sub-Class of UIView Class

@interface CircularProgressView : UIView
{
    CGFloat startAngle;
    CGFloat endAngle;
}
@property (nonatomic, assign) CGFloat percentage;
-(id) initWithFrame:(CGRect)frame;
@end

#import "CircularProgressView.h"

@implementation CircularProgressView

-(id) initWithFrame:(CGRect)frame{

  self = [super initWithFrame:frame];
    if (self) {
       
        startAngle = M_PI * 1.5;
        endAngle = startAngle + (M_PI*2);
    }
    return self;
}

// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    [[UIColor colorWithRed:(CGFloat)235/255 green:(CGFloat)237/255 blue:(CGFloat)236/255 alpha:1] setStroke];
    UIBezierPath *benzierPathBack = [UIBezierPath bezierPath];
    [benzierPathBack addArcWithCenter:CGPointMake(rect.size.width / 2, rect.size.height / 2) radius:(rect.size.width / 2 - 5) startAngle:startAngle endAngle:(endAngle - startAngle) * (100 / 100.0) + startAngle clockwise:YES];
   
    benzierPathBack.lineWidth = 10;
    [benzierPathBack stroke];
   
    CGContextSaveGState(context);
   
    UIBezierPath* bezierPath = [UIBezierPath bezierPath];
   
    // Create our arc, with the correct angles
    [bezierPath addArcWithCenter:CGPointMake(rect.size.width / 2, rect.size.height / 2) radius:(rect.size.width / 2-5) startAngle:startAngle endAngle:(endAngle - startAngle) * (_percentage / 100.0) + startAngle clockwise:YES];
   
    // Set the display for the path, and stroke it
    bezierPath.lineWidth = 10;
    [[UIColor colorWithRed:(CGFloat)1/255 green:(CGFloat)111/255 blue:(CGFloat)37/255 alpha:1] setStroke];
    [bezierPath stroke];  
}


Import CircularProgressView Class, create an object and change percentage value, you can see a 'Circular Progress View'.

Monday, 10 October 2016

CABasicAnimation

CABasicAnimation provides basic, single-keyframe animation capabilities for a layer property.
CABasicAnimation class animates a layer property between two values, a starting and an ending value. To move a layer from one point to another in its containing window, for example, we can create a basic animation using the keypath position. We give the animation a start value and an ending value and add the animation to the layer.

Here i am going to create a search animation, animation start with a small circle with minimum radius and continue increase the radius and after maximum radius again its start animate the circle from minimum radius.

First create  a Project(SearchActivityIndicatorViewSample)

Now create a new file(SearchActivityIndicatorView) which is subclass of UIView.
We are going to display a animated circle, so for creating a  circle i am going to declare another Class(SearchActivityIndicatorCircleView) inside "SearchActivityIndicatorView".
SearchActivityIndicatorCircleView class also a subclass of UIView.

Ok, Why i am creating a separate class for display a circle?
Answer is, if we want to display more than one circle as a search indicator so we can use multiple instance of that class and we don't need to write code for multiple circle. So by using this approach     we can avoid duplication of code.
 and also declare a property for stroke color inside "SearchActivityIndicatorCircleView Class".

here the code snippet

@interface SearchActivityIndicatorCircleView : UIView
{
   
}
@property (nonatomic, strong) UIColor *strokeColor;
@end


//Implementation 
@implementation SearchActivityIndicatorCircleView

@end


Add the following methods  inside SearchActivityIndicatorCircleView implementation.
-(instancetype) initWithFrame:(CGRect)frame{
    self = [super initWithFrame: frame];
    if (self) {
        self.backgroundColor = [UIColor clearColor];
    }
    return self;
}


-(void) drawRect:(CGRect)rect{
    //Drawing Code
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSaveGState(context);
    CGContextSetStrokeColorWithColor(context, self.strokeColor.CGColor);
    UIBezierPath *circlePath = [UIBezierPath bezierPath];
    [circlePath addArcWithCenter:CGPointMake(self.bounds.size.width/2, self.frame.size.height/2) radius:MIN(self.frame.size.width, self.frame.size.height)/2 - 5  startAngle:DEGREES_TO_RADIANS(0) endAngle:DEGREES_TO_RADIANS(360) clockwise:YES];
    CGPathRef cgPath = CGPathCreateCopy(circlePath.CGPath);
    CGContextAddPath(context, cgPath);
    CGContextSetLineWidth(context, 1);
    CGContextDrawPath(context, kCGPathStroke);
    CGPathRelease(cgPath);
}


In the first method i override initWithFrame: method for creating a new instance for the class.
and in the second method drawRect: i am creating a circle. Radius of circle is not static, its will depend on the frame size of View.
For calculating the radius i used smallest value between width and height and after getting smallest value reduce 5 from the that, and with the resultant value drawing circle.

Now we have enough code for draw a circle, only need to create a instance of a class and as a sub-view.

Now Switch to SearchActivityIndicatorView.h Class and add following codes.
@interface SearchActivityIndicatorView : UIView
{
    CGFloat maxScale, minScale;
}
@property (nonatomic, assign) BOOL isAnimating;
@property (nonatomic, strong) UIColor *strokeColor;
@property (nonatomic, assign) NSTimeInterval animationDuration;

-(void) startSearching;
-(void) stopSearching;


Now switch to SearchActivityIndicatorView.m and add following code
-(instancetype) init{
    self = [super init];
    if (self) {
        [self initializeDefaultValues];
    }
    return self;
}
-(instancetype) initWithFrame:(CGRect)frame{
   
    self = [super initWithFrame:frame];
    if (self) {
        [self initializeDefaultValues];
    }
    return self;
}
-(void) awakeFromNib{
    [super awakeFromNib];
    [self initializeDefaultValues];
}
-(void) initializeDefaultValues{
    //set animation suration
    self.animationDuration = 1.9f;
    //set default stroke color
    self.strokeColor = [UIColor redColor];
    //set minimum scale
    minScale = 0.3f;
    //set maximum scale

    maxScale = 8.0f;
}


First 3 methods are used to creating new instance for the class
and the last method set default value with the time of new instance creation of class.
Inside this method, i am defining default value for animation duration, color of circle' stroke, minimum and maximum scale for circle view.

Now its time to add SearchActivityIndicatorCircleView
-(void) addCircleSearchView{
   //1. get minimum value between width and height
    CGFloat width = MIN(self.frame.size.width, self.frame.size.height)/4;

   //2. Create an instance of SearchActivityIndicatorCircleView
    SearchActivityIndicatorCircleView *circleSearchView = [[SearchActivityIndicatorCircleView alloc] initWithFrame:CGRectMake(0, 0, width, width)];
    [circleSearchView setStrokeColor:self.strokeColor];
    circleSearchView.center = self.center;
    //3.Add CABasicAnimation for scale view
    CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
    //4. set animation duration time
    scaleAnimation.duration = self.animationDuration;
    //5. set repeat count to maximum
    scaleAnimation.repeatCount = CGFLOAT_MAX;
    //6. set minimum value for animation
    scaleAnimation.fromValue = @(minScale);
    //7. set maximum value for animation
    scaleAnimation.toValue = @(maxScale);
    //8. set timing function
    scaleAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];

    //9. add animation to Circle layer
    [circleSearchView.layer addAnimation:scaleAnimation forKey:kSearchScaleAnimationKey];
   //10. add SearchActivityIndicatorCircleView as a subview
    [self addSubview:circleSearchView];
}


Above method create an instance for  SearchActivityIndicatorCircleView, add basic animation for animate a circle and add newly created instances as a subview.
Here step by step explanations:
1. Find minimum value between width and height of frame and divide by 4 means the frame of circle view is 4 times less than SearchActivityIndicatorView.
2. Create an instance of  SearchActivityIndicatorCircleView with the minimum frame size.
3. Create an instance of CABasicAnimation with key "transform.scale".
4. set animation duration, the time require for animation of a circle from minimum scale to maximum scale.
5. set repeat count for animation.
6. set minimum scale or starting scale for animation.
7. set maximum scale for animating the circle.
8. timingFunction: A timing function defining the pacing of the animation.
    CAMediaTimingFunction represents one segment of a function describing a timing curve. The
    function maps an input time normalized to the range [0,1] to an
    output time also in the range [0,1]. E.g. these functions are used
    to define the pacing of an animation over its duration (or over the
   duration of one keyframe). Click here for more detail about CAMediaTimingFunction.

9. add animation to circle layer. CABasicAnimation provide capabilities for animate layer property, so we add animation to layer of circle view.
10. Add circle view as a subview.

We have code for draw a circle, add animation to circle.
So,  now its time to display animation, add below code in SearchActivityIndicatorCircleView.m

-(void) startSearching{
   //check if animation is already started
    if (self.isAnimating) {
        return;
    }

   //set flag value true
    self.isAnimating = YES;

  //add circle
    [self addCircleSearchView];
}


 first we are checking, is animation already started if yes than no need to start animation again.
If no than set the flag value true so we can check animation status later.
and last line send a message to  "addCircleSearchView" method for adding a circle view.




Also write a method for stop animation.
-(void) stopSearching{
    if(!self.isAnimating) {
        return;
    }
    for(UIView *searchView in self.subviews) {
        [UIView animateWithDuration:0.9f animations:^{
            searchView.transform = CGAffineTransformMakeScale(7.0f, 7.0f);
            [searchView setAlpha:0.0f];
        } completion:^(BOOL finished) {
            [searchView.layer removeAllAnimations];
            [searchView removeFromSuperview];
        }];
    }
    self.isAnimating = NO;
}


thats enough.
Now Its time for display our effort to end user.
Switch to ViewController.h class, import SearchActivityIndicatorView

add a property IBOutlet  for SearchActivityIndicatorView
@property (nonatomic, weak) IBOutlet SearchActivityIndicatorView *seachActivityIndicatorView;
and add a IBAction for handle user action.
-(IBAction)handleTapGesture:(UITapGestureRecognizer *)sender;

Now switch to  main.storyboard and drag a UIView from Object library.
Select that view and go to identity inspector and change the class from UIView to SearchActivityIndicatorView
 


Connect IBOutlet to SearchActivityIndicatorView.
Add a Tap Gesture Recognizer  to SearchActivityIndicatorView and connect IBAction method.

Now go to ViewController.m class and method for start animation
-(IBAction)handleTapGesture:(UITapGestureRecognizer *)sender{
   
    if (self.seachActivityIndicatorView.isAnimating == YES) {
        [self.seachActivityIndicatorView stopSearching];
    }
    else{
        [self.seachActivityIndicatorView startSearching];
    }
}


Run the app and tap on the screen, we can see a animated circle on screen.
Click here for complete project.