mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
306 lines
9.1 KiB
Objective-C
306 lines
9.1 KiB
Objective-C
#import "ProgressSpinnerView.h"
|
|
|
|
#define UIColorRGB(rgb) ([[UIColor alloc] initWithRed:(((rgb >> 16) & 0xff) / 255.0f) green:(((rgb >> 8) & 0xff) / 255.0f) blue:(((rgb) & 0xff) / 255.0f) alpha:1.0f])
|
|
|
|
@interface ProgressSpinnerViewInternal : UIView
|
|
|
|
@property (nonatomic, copy) void (^onDraw)(void);
|
|
@property (nonatomic, copy) void (^onSuccess)(void);
|
|
|
|
- (instancetype)initWithFrame:(CGRect)frame light:(bool)light;
|
|
|
|
- (void)setProgress;
|
|
- (void)setSucceed:(bool)fromRotation progress:(CGFloat)progress;
|
|
|
|
@end
|
|
|
|
@interface ProgressSpinnerView ()
|
|
{
|
|
UIImageView *_arcView;
|
|
ProgressSpinnerViewInternal *_internalView;
|
|
|
|
bool _progressing;
|
|
}
|
|
@end
|
|
|
|
@implementation ProgressSpinnerView
|
|
|
|
- (instancetype)initWithFrame:(CGRect)frame light:(bool)light {
|
|
self = [super initWithFrame:frame];
|
|
if (self != nil) {
|
|
self.backgroundColor = [UIColor clearColor];
|
|
self.opaque = false;
|
|
self.userInteractionEnabled = false;
|
|
|
|
UIImage *arcImage = nil;
|
|
CGRect rect = CGRectMake(0.0f, 0.0f, frame.size.width, frame.size.height);
|
|
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0f);
|
|
CGContextRef context = UIGraphicsGetCurrentContext();
|
|
|
|
CGPoint centerPoint = CGPointMake(rect.size.width / 2.0f, rect.size.height / 2.0f);
|
|
CGFloat lineWidth = 4.0f;
|
|
CGFloat inset = 3.0f;
|
|
|
|
UIColor *foregroundColor = light ? UIColorRGB(0x5a5a5a) : [UIColor whiteColor];
|
|
CGContextSetFillColorWithColor(context, foregroundColor.CGColor);
|
|
CGContextSetStrokeColorWithColor(context, foregroundColor.CGColor);
|
|
|
|
CGMutablePathRef path = CGPathCreateMutable();
|
|
CGFloat offset = -2.0f * M_PI;
|
|
CGPathAddArc(path, NULL, centerPoint.x, centerPoint.y, (rect.size.width - inset * 2.0f - lineWidth) / 2.0f, offset, offset + (3.0f * M_PI_2), false);
|
|
CGPathRef strokedArc = CGPathCreateCopyByStrokingPath(path, NULL, lineWidth, kCGLineCapRound, kCGLineJoinMiter, 10);
|
|
CGContextAddPath(context, strokedArc);
|
|
CGPathRelease(strokedArc);
|
|
CGPathRelease(path);
|
|
|
|
CGContextFillPath(context);
|
|
|
|
arcImage = UIGraphicsGetImageFromCurrentImageContext();
|
|
UIGraphicsEndImageContext();
|
|
//});
|
|
|
|
_arcView = [[UIImageView alloc] initWithFrame:self.bounds];
|
|
_arcView.image = arcImage;
|
|
_arcView.hidden = true;
|
|
[self addSubview:_arcView];
|
|
|
|
_internalView = [[ProgressSpinnerViewInternal alloc] initWithFrame:self.bounds light:light];
|
|
_internalView.hidden = true;
|
|
[self addSubview:_internalView];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void)setProgress {
|
|
_arcView.hidden = false;
|
|
_progressing = true;
|
|
|
|
CABasicAnimation *rotationAnimation;
|
|
rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
|
|
rotationAnimation.toValue = @(-M_PI * 2.0f);
|
|
rotationAnimation.duration = 0.75;
|
|
rotationAnimation.cumulative = true;
|
|
rotationAnimation.repeatCount = HUGE_VALF;
|
|
|
|
[_arcView.layer addAnimation:rotationAnimation forKey:@"rotationAnimation"];
|
|
}
|
|
|
|
- (void)setSucceed {
|
|
_internalView.hidden = false;
|
|
|
|
if (_progressing)
|
|
{
|
|
CGFloat value = [[_arcView.layer.presentationLayer valueForKeyPath:@"transform.rotation.z"] doubleValue] / (-2 * M_PI);
|
|
[_internalView setSucceed:_progressing progress:value];
|
|
|
|
__weak ProgressSpinnerView *weakSelf = self;
|
|
_internalView.onDraw = ^{
|
|
__strong ProgressSpinnerView *strongSelf = weakSelf;
|
|
if (strongSelf != nil)
|
|
strongSelf->_arcView.hidden = true;
|
|
};
|
|
_internalView.onSuccess = ^{
|
|
__strong ProgressSpinnerView *strongSelf = weakSelf;
|
|
if (strongSelf != nil && strongSelf.onSuccess != nil)
|
|
strongSelf.onSuccess();
|
|
};
|
|
}
|
|
else
|
|
{
|
|
[_internalView setSucceed:false progress:0.0f];
|
|
}
|
|
}
|
|
|
|
@end
|
|
|
|
@interface ProgressSpinnerViewInternal ()
|
|
{
|
|
CADisplayLink *_displayLink;
|
|
|
|
bool _light;
|
|
|
|
bool _isProgressing;
|
|
CGFloat _rotationValue;
|
|
bool _isRotating;
|
|
|
|
CGFloat _checkValue;
|
|
bool _delay;
|
|
bool _isSucceed;
|
|
bool _isChecking;
|
|
|
|
NSTimeInterval _previousTime;
|
|
}
|
|
@end
|
|
|
|
@implementation ProgressSpinnerViewInternal
|
|
|
|
- (instancetype)initWithFrame:(CGRect)frame light:(bool)light {
|
|
self = [super initWithFrame:frame];
|
|
if (self != nil) {
|
|
_light = light;
|
|
|
|
self.backgroundColor = [UIColor clearColor];
|
|
self.opaque = false;
|
|
self.userInteractionEnabled = false;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void)dealloc {
|
|
_displayLink.paused = true;
|
|
[_displayLink removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
|
|
}
|
|
|
|
- (CADisplayLink *)displayLink {
|
|
if (_displayLink == nil) {
|
|
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(displayLinkUpdate)];
|
|
_displayLink.paused = true;
|
|
[_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
|
|
}
|
|
return _displayLink;
|
|
}
|
|
|
|
- (void)drawRect:(CGRect)rect {
|
|
CGContextRef context = UIGraphicsGetCurrentContext();
|
|
|
|
CGPoint centerPoint = CGPointMake(rect.size.width / 2.0f, rect.size.height / 2.0f);
|
|
CGFloat lineWidth = 4.0f;
|
|
CGFloat inset = 3.0f;
|
|
if (rect.size.width < 44.0) {
|
|
inset = 0.0f;
|
|
}
|
|
|
|
UIColor *foregroundColor = _light ? UIColorRGB(0x5a5a5a) : [UIColor whiteColor];
|
|
CGContextSetFillColorWithColor(context, foregroundColor.CGColor);
|
|
CGContextSetStrokeColorWithColor(context, foregroundColor.CGColor);
|
|
|
|
if (_isProgressing)
|
|
{
|
|
CGMutablePathRef path = CGPathCreateMutable();
|
|
CGFloat offset = -_rotationValue * 2.0f * M_PI;
|
|
CGPathAddArc(path, NULL, centerPoint.x, centerPoint.y, (rect.size.width - inset * 2.0f - lineWidth) / 2.0f, offset, offset + (3.0f * M_PI_2) * (1.0f - _checkValue), false);
|
|
CGPathRef strokedArc = CGPathCreateCopyByStrokingPath(path, NULL, lineWidth, kCGLineCapRound, kCGLineJoinMiter, 10);
|
|
CGContextAddPath(context, strokedArc);
|
|
CGPathRelease(strokedArc);
|
|
CGPathRelease(path);
|
|
|
|
CGContextFillPath(context);
|
|
}
|
|
|
|
if (_checkValue > FLT_EPSILON)
|
|
{
|
|
CGContextSetLineWidth(context, 5.0f);
|
|
CGContextSetLineCap(context, kCGLineCapRound);
|
|
CGContextSetLineJoin(context, kCGLineJoinRound);
|
|
CGContextSetMiterLimit(context, 10);
|
|
|
|
CGFloat firstSegment = MIN(1.0f, _checkValue * 3.0f);
|
|
CGPoint s = CGPointMake(inset + 5.0f / 2.0f, centerPoint.y);
|
|
CGPoint p1 = CGPointMake(13.0f, 13.0f);
|
|
CGPoint p2 = CGPointMake(27.0f, -27.0f);
|
|
|
|
if (firstSegment < 1.0f)
|
|
{
|
|
CGContextMoveToPoint(context, s.x + p1.x * firstSegment, s.y + p1.y * firstSegment);
|
|
CGContextAddLineToPoint(context, s.x, s.y);
|
|
}
|
|
else
|
|
{
|
|
CGFloat secondSegment = (_checkValue - 0.33f) * 1.5f;
|
|
CGContextMoveToPoint(context, s.x + p1.x + p2.x * secondSegment, s.y + p1.y + p2.y * secondSegment);
|
|
CGContextAddLineToPoint(context, s.x + p1.x, s.y + p1.y);
|
|
CGContextAddLineToPoint(context, s.x, s.y);
|
|
}
|
|
|
|
CGContextStrokePath(context);
|
|
}
|
|
}
|
|
|
|
- (void)displayLinkUpdate
|
|
{
|
|
NSTimeInterval previousTime = _previousTime;
|
|
NSTimeInterval currentTime = CACurrentMediaTime();
|
|
_previousTime = currentTime;
|
|
|
|
NSTimeInterval delta = previousTime > DBL_EPSILON ? currentTime - previousTime : 0.0;
|
|
if (delta < DBL_EPSILON)
|
|
return;
|
|
|
|
if (_isRotating)
|
|
{
|
|
_rotationValue += delta * 1.35f;
|
|
}
|
|
|
|
if (_isSucceed && _isRotating && !_delay && _rotationValue >= 0.5f)
|
|
{
|
|
_rotationValue = 0.5f;
|
|
_isRotating = false;
|
|
_isChecking = true;
|
|
}
|
|
|
|
if (_isChecking)
|
|
_checkValue += delta * M_PI * 1.6f;
|
|
|
|
if (_rotationValue > 1.0f)
|
|
{
|
|
_rotationValue = 0.0f;
|
|
_delay = false;
|
|
}
|
|
|
|
if (_checkValue > 1.0f)
|
|
{
|
|
_checkValue = 1.0f;
|
|
[self displayLink].paused = true;
|
|
|
|
if (self.onSuccess != nil)
|
|
{
|
|
void (^onSuccess)(void) = [self.onSuccess copy];
|
|
self.onSuccess = nil;
|
|
onSuccess();
|
|
}
|
|
}
|
|
|
|
[self setNeedsDisplay];
|
|
|
|
if (self.onDraw != nil)
|
|
{
|
|
void (^onDraw)(void) = [self.onDraw copy];
|
|
self.onDraw = nil;
|
|
onDraw();
|
|
}
|
|
}
|
|
|
|
- (void)setProgress {
|
|
_isRotating = true;
|
|
_isProgressing = true;
|
|
|
|
[self displayLink].paused = false;
|
|
}
|
|
|
|
- (void)setSucceed:(bool)fromRotation progress:(CGFloat)progress {
|
|
if (_isSucceed)
|
|
return;
|
|
|
|
if (fromRotation) {
|
|
_isRotating = true;
|
|
_isProgressing = true;
|
|
_rotationValue = progress;
|
|
}
|
|
|
|
_isSucceed = true;
|
|
|
|
if (!_isRotating)
|
|
_isChecking = true;
|
|
else if (_rotationValue > 0.5f)
|
|
_delay = true;
|
|
|
|
[self displayLink].paused = false;
|
|
}
|
|
|
|
- (bool)isSucceed
|
|
{
|
|
return _isSucceed;
|
|
}
|
|
|
|
@end
|