Skip to content

Commit

Permalink
Animations on operation start and finish added
Browse files Browse the repository at this point in the history
  • Loading branch information
daria-kopaliani committed Aug 27, 2013
1 parent b0f6f54 commit a3f984c
Show file tree
Hide file tree
Showing 6 changed files with 169 additions and 53 deletions.
42 changes: 34 additions & 8 deletions DAProgressOverlayView/DAProgressOverlayView.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,47 @@

@property (strong, nonatomic) UIColor *overlayColor;

/*
ratio of inner circle to the minimum side of DAProgressOverlayView
0 ≤ innerRadiusRatio ≤ 1
/**
The ratio of the inner circle to the minimum side of DAProgressOverlayView,
0 ≤ innerRadiusRatio ≤ 1,
This is #000000 with alpha equal 0.5 by default.
*/
@property (assign, nonatomic) CGFloat innerRadiusRatio;

/*
ratio of outer circle to the minimum side of DAProgressOverlayView
0 ≤ outerRadiusRatio ≤ 1
/**
The ratio of the outer circle to the minimum side of DAProgressOverlayView,
0 ≤ outerRadiusRatio ≤ 1,
This is 0.7 by default.
*/

@property (assign, nonatomic) CGFloat outerRadiusRatio;

/*
0 ≤ progress ≤ 1
/**
The float value used to for calculate the `filled in` fraction of the inner circle,
0 ≤ progress ≤ 1.
*/
@property (assign, nonatomic) CGFloat progress;

/**
The duration for animations displayed after calling `displayOperationWillTriggerAnimation` and `displayOperationDidFinishAnimation` methods.
This is 0.25 by default.
*/
@property (assign, nonatomic) CGFloat stateChangeAnimationDuration;

/**
This flag indicates wheter or not 'displayDownloadDidFinishAnimation' method is called when 'progress' property is set to 1.,
This is YES by default.
*/
@property (assign, nonatomic) BOOL triggersDownloadDidFinishAnimationAutomatically;

/**
makes the outer faded out circle radius expand until it circumscribes the DAProgressOverlayView bounds
*/
- (void)displayOperationDidFinishAnimation;

/**
Changes radiuses of the inner and outer circles from zero to the corresponding values, calculated from 'innerRadiusRatio' and 'outerRadiusRatio' properties.
*/
- (void)displayOperationWillTriggerAnimation;

@end
143 changes: 110 additions & 33 deletions DAProgressOverlayView/DAProgressOverlayView.m
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,24 @@

#import "DAProgressOverlayView.h"

typedef enum {
DAProgressOverlayViewStateWaiting = 0,
DAProgressOverlayViewStateOperationInProgress = 1,
DAProgressOverlayViewStateOperationFinished = 2
} DAProgressOverlayViewState;

@interface DAProgressOverlayView ()

@property (assign, nonatomic) DAProgressOverlayViewState state;
@property (assign, nonatomic) CGFloat animationProggress;
@property (strong, nonatomic) NSTimer *timer;

@end


CGFloat const DAUpdateUIFrequency = 1. / 25.;


@implementation DAProgressOverlayView

#pragma mark - Initialization
Expand All @@ -33,82 +51,141 @@ - (id)initWithFrame:(CGRect)frame
- (void)setUp
{
self.backgroundColor = [UIColor clearColor];
self.progress = 0;
self.progress = 0.;
self.outerRadiusRatio = 0.7;
self.innerRadiusRatio = 0.6;
self.overlayColor = [UIColor colorWithRed:0. green:0. blue:0. alpha:0.3];
self.overlayColor = [UIColor colorWithRed:0. green:0. blue:0. alpha:0.5];
self.animationProggress = 0.;
self.stateChangeAnimationDuration = 0.25;
self.triggersDownloadDidFinishAnimationAutomatically = YES;
}

#pragma mark - Public

- (void)setInnerRadiusRatio:(CGFloat)innerRadiusRatio
- (void)displayOperationDidFinishAnimation
{
_innerRadiusRatio = (innerRadiusRatio < 0.) ? 0. : (innerRadiusRatio > 1.) ? 1. : innerRadiusRatio;
}

- (void)setOuterRadiusRatio:(CGFloat)outerRadiusRatio
{
_outerRadiusRatio = (outerRadiusRatio < 0.) ? 0. : (outerRadiusRatio > 1.) ? 1. : outerRadiusRatio;
self.state = DAProgressOverlayViewStateOperationFinished;
self.animationProggress = 0.;
self.timer = [NSTimer scheduledTimerWithTimeInterval:DAUpdateUIFrequency target:self selector:@selector(update) userInfo:nil repeats:YES];
}

- (void)setProgress:(CGFloat)progress
- (void)displayOperationWillTriggerAnimation
{
if (_progress != progress) {
_progress = (progress < 0.) ? 0. : (progress > 1.) ? 1. : progress;
[self setNeedsDisplay];
}
self.state = DAProgressOverlayViewStateWaiting;
self.animationProggress = 0.;
self.timer = [NSTimer scheduledTimerWithTimeInterval:DAUpdateUIFrequency target:self selector:@selector(update) userInfo:nil repeats:YES];
}

#pragma mark - Private
#pragma mark * Overwritten methods

- (void)drawRect:(CGRect)rect
{
CGFloat width = CGRectGetWidth(rect);
CGFloat height = CGRectGetHeight(rect);

CGFloat outerRadius = MIN(width, height) / 2. * self.outerRadiusRatio;
CGFloat innerRadius = MIN(width, height) / 2. * self.innerRadiusRatio;

CGFloat outerRadius = [self outerRadius];
CGFloat innerRadius = [self innerRadius];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, width / 2., height / 2.);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextSetRGBFillColor(context, 0.0, 0.0, 0.0, 0.5);
CGContextScaleCTM(context, 1., -1.);
CGContextSetRGBFillColor(context, 0., 0., 0., 0.5);
CGContextSetFillColorWithColor(context, self.overlayColor.CGColor);

CGMutablePathRef path0 = CGPathCreateMutable();
CGPathMoveToPoint(path0, NULL, width / 2., 0);
CGPathMoveToPoint(path0, NULL, width / 2., 0.);
CGPathAddLineToPoint(path0, NULL, width / 2., height / 2.);
CGPathAddLineToPoint(path0, NULL, -width / 2., height / 2.);
CGPathAddLineToPoint(path0, NULL, -width / 2., 0);
CGPathAddLineToPoint(path0, NULL, (cosf(M_PI) * outerRadius), 0);
CGPathAddArc(path0, NULL, 0, 0, outerRadius, M_PI, 0, 1);
CGPathAddLineToPoint(path0, NULL, width / 2., 0);
CGPathAddLineToPoint(path0, NULL, -width / 2., 0.);
CGPathAddLineToPoint(path0, NULL, (cosf(M_PI) * outerRadius), 0.);
CGPathAddArc(path0, NULL, 0., 0., outerRadius, M_PI, 0., 1.);
CGPathAddLineToPoint(path0, NULL, width / 2., 0.);
CGPathCloseSubpath(path0);

CGMutablePathRef path1 = CGPathCreateMutable();
CGAffineTransform rotation = CGAffineTransformMakeScale(1.0, -1.0);
CGAffineTransform rotation = CGAffineTransformMakeScale(1., -1.);
CGPathAddPath(path1, &rotation, path0);

CGContextAddPath(context, path0);
CGContextFillPath(context);
CGPathRelease(path0);

CGContextAddPath(context, path1);
CGContextFillPath(context);
CGPathRelease(path1);

if (_progress < 1.) {
CGFloat angle = 360. - (360. * _progress);
CGAffineTransform transform = CGAffineTransformMakeRotation(M_PI_2);
CGMutablePathRef path2 = CGPathCreateMutable();
CGPathMoveToPoint(path2, &transform, innerRadius, 0);
CGPathAddArc(path2, &transform, 0, 0, innerRadius, 0, angle / 180. * M_PI, 0);
CGPathAddLineToPoint(path2, &transform, 0, 0);
CGPathAddLineToPoint(path2, &transform, innerRadius, 0);
CGPathMoveToPoint(path2, &transform, innerRadius, 0.);
CGPathAddArc(path2, &transform, 0., 0., innerRadius, 0., angle / 180. * M_PI, 0.);
CGPathAddLineToPoint(path2, &transform, 0., 0.);
CGPathAddLineToPoint(path2, &transform, innerRadius, 0.);
CGContextAddPath(context, path2);
CGContextFillPath(context);
CGPathRelease(path2);
}
}

- (void)setInnerRadiusRatio:(CGFloat)innerRadiusRatio
{
_innerRadiusRatio = (innerRadiusRatio < 0.) ? 0. : (innerRadiusRatio > 1.) ? 1. : innerRadiusRatio;
}

- (void)setOuterRadiusRatio:(CGFloat)outerRadiusRatio
{
_outerRadiusRatio = (outerRadiusRatio < 0.) ? 0. : (outerRadiusRatio > 1.) ? 1. : outerRadiusRatio;
}

- (void)setProgress:(CGFloat)progress
{
if (_progress != progress) {
_progress = (progress < 0.) ? 0. : (progress > 1.) ? 1. : progress;
if (progress > 0. && progress < 1.) {
self.state = DAProgressOverlayViewStateOperationInProgress;
[self setNeedsDisplay];
} else if (progress == 1. && self.triggersDownloadDidFinishAnimationAutomatically) {
[self displayOperationDidFinishAnimation];
}
}
}

#pragma mark - Private

- (CGFloat)innerRadius
{
CGFloat width = CGRectGetWidth(self.bounds);
CGFloat height = CGRectGetHeight(self.bounds);
CGFloat radius = MIN(width, height) / 2. * self.innerRadiusRatio;
switch (self.state) {
case DAProgressOverlayViewStateWaiting: return radius * self.animationProggress;
case DAProgressOverlayViewStateOperationFinished: return radius + (MAX(width, height) / sqrtf(2.) - radius) * self.animationProggress;
default: return radius;
}
}

- (CGFloat)outerRadius
{
CGFloat width = CGRectGetWidth(self.bounds);
CGFloat height = CGRectGetHeight(self.bounds);
CGFloat radius = MIN(width, height) / 2. * self.outerRadiusRatio;
switch (self.state) {
case DAProgressOverlayViewStateWaiting: return radius * self.animationProggress;
case DAProgressOverlayViewStateOperationFinished: return radius + (MAX(width, height) / sqrtf(2.) - radius) * self.animationProggress;
default: return radius;
}
}

- (void)update
{
CGFloat animationProggress = self.animationProggress + DAUpdateUIFrequency / self.stateChangeAnimationDuration;
if (animationProggress >= 1.) {
self.animationProggress = 1.;
[self.timer invalidate];
} else {
self.animationProggress = animationProggress;
}
[self setNeedsDisplay];
}

@end
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,7 @@
436C6BAA17AAB275009CC36C /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
436C6BAB17AAB275009CC36C /* Build configuration list for PBXNativeTarget "DAProgressOverlayViewDemoTests" */ = {
isa = XCConfigurationList;
Expand All @@ -487,6 +488,7 @@
436C6BAD17AAB275009CC36C /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<string>1.0.2</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<string>1.0.2</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIMainStoryboardFile</key>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#import <QuartzCore/QuartzCore.h>
#import "DAProgressOverlayView.h"


@interface DAViewController ()

@property (strong, nonatomic) IBOutlet UIImageView *imageView;
Expand All @@ -20,6 +21,7 @@ @interface DAViewController ()

@end


@implementation DAViewController

- (void)viewDidLoad
Expand All @@ -29,31 +31,35 @@ - (void)viewDidLoad
self.imageView.layer.cornerRadius = 35.;
self.progressOverlayView = [[DAProgressOverlayView alloc] initWithFrame:self.imageView.bounds];
[self.imageView addSubview:self.progressOverlayView];
self.progressOverlayView.hidden = YES;

}

- (IBAction)downloadButtonTapped:(id)sender
{
self.downloadButton.enabled = NO;
[self.downloadButton setTitle:@"Downloading..." forState:UIControlStateNormal];
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.02 target:self selector:@selector(updateProgress) userInfo:nil repeats:YES];
self.progressOverlayView.hidden = NO;
[self.progressOverlayView displayOperationWillTriggerAnimation];
double delayInSeconds = self.progressOverlayView.stateChangeAnimationDuration;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void) {
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.02 target:self selector:@selector(updateProgress) userInfo:nil repeats:YES];
});
}

- (void)updateProgress
{
CGFloat progress = self.progressOverlayView.progress + 0.005;
if (progress > 1) {
CGFloat progress = self.progressOverlayView.progress + 0.01;
if (progress >= 1) {
[self.timer invalidate];
[UIView animateWithDuration:0.2 delay:0. options:UIViewAnimationOptionCurveEaseOut animations:^{
self.progressOverlayView.alpha = 0.;
} completion:^(BOOL finished) {
[self.progressOverlayView displayOperationDidFinishAnimation];
double delayInSeconds = self.progressOverlayView.stateChangeAnimationDuration;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
self.progressOverlayView.progress = 0.;
self.progressOverlayView.alpha = 1.;
[self.downloadButton setTitle:@"Download" forState:UIControlStateNormal];
self.progressOverlayView.hidden = YES;
self.downloadButton.enabled = YES;
}];
});
} else {
self.progressOverlayView.progress = progress;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
Expand Down

0 comments on commit a3f984c

Please sign in to comment.