create
21
Pods/SVProgressHUD/LICENSE
generated
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2011-2018 Sam Vermette, Tobias Tiemerding and contributors.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
218
Pods/SVProgressHUD/README.md
generated
Normal file
@@ -0,0 +1,218 @@
|
||||
# SVProgressHUD
|
||||
|
||||

|
||||

|
||||

|
||||
[](https://github.com/Carthage/Carthage)
|
||||
[](https://cocoapods.org)
|
||||
|
||||
`SVProgressHUD` is a clean and easy-to-use HUD meant to display the progress of an ongoing task on iOS and tvOS.
|
||||
|
||||

|
||||
|
||||
## Demo
|
||||
|
||||
Try `SVProgressHUD` on [Appetize.io](https://appetize.io/app/p8r2cvy8kq74x7q7tjqf5gyatr).
|
||||
|
||||
## Installation
|
||||
|
||||
### From CocoaPods
|
||||
|
||||
[CocoaPods](http://cocoapods.org) is a dependency manager for Objective-C, which automates and simplifies the process of using 3rd-party libraries like `SVProgressHUD` in your projects. First, add the following line to your [Podfile](http://guides.cocoapods.org/using/using-cocoapods.html):
|
||||
|
||||
```ruby
|
||||
pod 'SVProgressHUD'
|
||||
```
|
||||
|
||||
If you want to use the latest features of `SVProgressHUD` use normal external source dependencies.
|
||||
|
||||
```ruby
|
||||
pod 'SVProgressHUD', :git => 'https://github.com/SVProgressHUD/SVProgressHUD.git'
|
||||
```
|
||||
|
||||
This pulls from the `master` branch directly.
|
||||
|
||||
Second, install `SVProgressHUD` into your project:
|
||||
|
||||
```ruby
|
||||
pod install
|
||||
```
|
||||
|
||||
### Carthage
|
||||
|
||||
[Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks. To integrate `SVProgressHUD` into your Xcode project using Carthage, specify it in your `Cartfile`:
|
||||
|
||||
```ogdl
|
||||
github "SVProgressHUD/SVProgressHUD"
|
||||
```
|
||||
|
||||
Run `carthage bootstrap` to build the framework in your repository's Carthage directory. You can then include it in your target's `carthage copy-frameworks` build phase. For more information on this, please see [Carthage's documentation](https://github.com/carthage/carthage#if-youre-building-for-ios-tvos-or-watchos).
|
||||
|
||||
### Manually
|
||||
|
||||
* Drag the `SVProgressHUD/SVProgressHUD` folder into your project.
|
||||
* Take care that `SVProgressHUD.bundle` is added to `Targets->Build Phases->Copy Bundle Resources`.
|
||||
* Add the **QuartzCore** framework to your project.
|
||||
|
||||
## Swift
|
||||
|
||||
Even though `SVProgressHUD` is written in Objective-C, it can be used in Swift with no hassle. If you use [CocoaPods](http://cocoapods.org) add the following line to your [Podfile](http://guides.cocoapods.org/using/using-cocoapods.html):
|
||||
|
||||
```ruby
|
||||
use_frameworks!
|
||||
```
|
||||
|
||||
If you added `SVProgressHUD` manually, just add a [bridging header](https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html) file to your project with the `SVProgressHUD` header included.
|
||||
|
||||
## Usage
|
||||
|
||||
(see sample Xcode project in `/Demo`)
|
||||
|
||||
`SVProgressHUD` is created as a singleton (i.e. it doesn't need to be explicitly allocated and instantiated; you directly call `[SVProgressHUD method]`).
|
||||
|
||||
**Use `SVProgressHUD` wisely! Only use it if you absolutely need to perform a task before taking the user forward. Bad use case examples: pull to refresh, infinite scrolling, sending message.**
|
||||
|
||||
Using `SVProgressHUD` in your app will usually look as simple as this (using Grand Central Dispatch):
|
||||
|
||||
```objective-c
|
||||
[SVProgressHUD show];
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
// time-consuming task
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[SVProgressHUD dismiss];
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Showing the HUD
|
||||
|
||||
You can show the status of indeterminate tasks using one of the following:
|
||||
|
||||
```objective-c
|
||||
+ (void)show;
|
||||
+ (void)showWithStatus:(NSString*)string;
|
||||
```
|
||||
|
||||
If you'd like the HUD to reflect the progress of a task, use one of these:
|
||||
|
||||
```objective-c
|
||||
+ (void)showProgress:(CGFloat)progress;
|
||||
+ (void)showProgress:(CGFloat)progress status:(NSString*)status;
|
||||
```
|
||||
|
||||
### Dismissing the HUD
|
||||
|
||||
The HUD can be dismissed using:
|
||||
|
||||
```objective-c
|
||||
+ (void)dismiss;
|
||||
+ (void)dismissWithDelay:(NSTimeInterval)delay;
|
||||
```
|
||||
|
||||
If you'd like to stack HUDs, you can balance out every show call using:
|
||||
|
||||
```
|
||||
+ (void)popActivity;
|
||||
```
|
||||
|
||||
The HUD will get dismissed once the popActivity calls will match the number of show calls.
|
||||
|
||||
Or show a confirmation glyph before before getting dismissed a little bit later. The display time depends on `minimumDismissTimeInterval` and the length of the given string.
|
||||
|
||||
```objective-c
|
||||
+ (void)showInfoWithStatus:(NSString*)string;
|
||||
+ (void)showSuccessWithStatus:(NSString*)string;
|
||||
+ (void)showErrorWithStatus:(NSString*)string;
|
||||
+ (void)showImage:(UIImage*)image status:(NSString*)string;
|
||||
```
|
||||
|
||||
## Customization
|
||||
|
||||
`SVProgressHUD` can be customized via the following methods:
|
||||
|
||||
```objective-c
|
||||
+ (void)setDefaultStyle:(SVProgressHUDStyle)style; // default is SVProgressHUDStyleLight
|
||||
+ (void)setDefaultMaskType:(SVProgressHUDMaskType)maskType; // default is SVProgressHUDMaskTypeNone
|
||||
+ (void)setDefaultAnimationType:(SVProgressHUDAnimationType)type; // default is SVProgressHUDAnimationTypeFlat
|
||||
+ (void)setContainerView:(UIView*)containerView; // default is window level
|
||||
+ (void)setMinimumSize:(CGSize)minimumSize; // default is CGSizeZero, can be used to avoid resizing
|
||||
+ (void)setRingThickness:(CGFloat)width; // default is 2 pt
|
||||
+ (void)setRingRadius:(CGFloat)radius; // default is 18 pt
|
||||
+ (void)setRingNoTextRadius:(CGFloat)radius; // default is 24 pt
|
||||
+ (void)setCornerRadius:(CGFloat)cornerRadius; // default is 14 pt
|
||||
+ (void)setBorderColor:(nonnull UIColor*)color; // default is nil
|
||||
+ (void)setBorderWidth:(CGFloat)width; // default is 0
|
||||
+ (void)setFont:(UIFont*)font; // default is [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline]
|
||||
+ (void)setForegroundColor:(UIColor*)color; // default is [UIColor blackColor], only used for SVProgressHUDStyleCustom
|
||||
+ (void)setBackgroundColor:(UIColor*)color; // default is [UIColor whiteColor], only used for SVProgressHUDStyleCustom
|
||||
+ (void)setBackgroundLayerColor:(UIColor*)color; // default is [UIColor colorWithWhite:0 alpha:0.4], only used for SVProgressHUDMaskTypeCustom
|
||||
+ (void)setImageViewSize:(CGSize)size; // default is 28x28 pt
|
||||
+ (void)setShouldTintImages:(BOOL)shouldTintImages; // default is YES
|
||||
+ (void)setInfoImage:(UIImage*)image; // default is the bundled info image provided by Freepik
|
||||
+ (void)setSuccessImage:(UIImage*)image; // default is bundled success image from Freepik
|
||||
+ (void)setErrorImage:(UIImage*)image; // default is bundled error image from Freepik
|
||||
+ (void)setViewForExtension:(UIView*)view; // default is nil, only used if #define SV_APP_EXTENSIONS is set
|
||||
+ (void)setGraceTimeInterval:(NSTimeInterval)interval; // default is 0 seconds
|
||||
+ (void)setMinimumDismissTimeInterval:(NSTimeInterval)interval; // default is 5.0 seconds
|
||||
+ (void)setMaximumDismissTimeInterval:(NSTimeInterval)interval; // default is CGFLOAT_MAX
|
||||
+ (void)setFadeInAnimationDuration:(NSTimeInterval)duration; // default is 0.15 seconds
|
||||
+ (void)setFadeOutAnimationDuration:(NSTimeInterval)duration; // default is 0.15 seconds
|
||||
+ (void)setMaxSupportedWindowLevel:(UIWindowLevel)windowLevel; // default is UIWindowLevelNormal
|
||||
+ (void)setHapticsEnabled:(BOOL)hapticsEnabled; // default is NO
|
||||
```
|
||||
|
||||
Additionally `SVProgressHUD` supports the `UIAppearance` protocol for most of the above methods.
|
||||
|
||||
### Hint
|
||||
|
||||
As standard `SVProgressHUD` offers two preconfigured styles:
|
||||
|
||||
* `SVProgressHUDStyleLight`: White background with black spinner and text
|
||||
* `SVProgressHUDStyleDark`: Black background with white spinner and text
|
||||
|
||||
If you want to use custom colors use `setForegroundColor` and `setBackgroundColor:`. These implicitly set the HUD's style to `SVProgressHUDStyleCustom`.
|
||||
|
||||
## Haptic Feedback
|
||||
|
||||
For users with newer devices (starting with the iPhone 7), `SVProgressHUD` can automatically trigger haptic feedback depending on which HUD is being displayed. The feedback maps as follows:
|
||||
|
||||
* `showSuccessWithStatus:` <-> `UINotificationFeedbackTypeSuccess`
|
||||
* `showInfoWithStatus:` <-> `UINotificationFeedbackTypeWarning`
|
||||
* `showErrorWithStatus:` <-> `UINotificationFeedbackTypeError`
|
||||
|
||||
To enable this functionality, use `setHapticsEnabled:`.
|
||||
|
||||
Users with devices prior to iPhone 7 will have no change in functionality.
|
||||
|
||||
## Notifications
|
||||
|
||||
`SVProgressHUD` posts four notifications via `NSNotificationCenter` in response to being shown/dismissed:
|
||||
* `SVProgressHUDWillAppearNotification` when the show animation starts
|
||||
* `SVProgressHUDDidAppearNotification` when the show animation completes
|
||||
* `SVProgressHUDWillDisappearNotification` when the dismiss animation starts
|
||||
* `SVProgressHUDDidDisappearNotification` when the dismiss animation completes
|
||||
|
||||
Each notification passes a `userInfo` dictionary holding the HUD's status string (if any), retrievable via `SVProgressHUDStatusUserInfoKey`.
|
||||
|
||||
`SVProgressHUD` also posts `SVProgressHUDDidReceiveTouchEventNotification` when users touch on the overall screen or `SVProgressHUDDidTouchDownInsideNotification` when a user touches on the HUD directly. For this notifications `userInfo` is not passed but the object parameter contains the `UIEvent` that related to the touch.
|
||||
|
||||
## App Extensions
|
||||
|
||||
When using `SVProgressHUD` in an App Extension, `#define SV_APP_EXTENSIONS` to avoid using unavailable APIs. Additionally call `setViewForExtension:` from your extensions view controller with `self.view`.
|
||||
|
||||
## Contributing to this project
|
||||
|
||||
If you have feature requests or bug reports, feel free to help out by sending pull requests or by [creating new issues](https://github.com/SVProgressHUD/SVProgressHUD/issues/new). Please take a moment to
|
||||
review the guidelines written by [Nicolas Gallagher](https://github.com/necolas):
|
||||
|
||||
* [Bug reports](https://github.com/necolas/issue-guidelines/blob/master/CONTRIBUTING.md#bugs)
|
||||
* [Feature requests](https://github.com/necolas/issue-guidelines/blob/master/CONTRIBUTING.md#features)
|
||||
* [Pull requests](https://github.com/necolas/issue-guidelines/blob/master/CONTRIBUTING.md#pull-requests)
|
||||
|
||||
## License
|
||||
|
||||
`SVProgressHUD` is distributed under the terms and conditions of the [MIT license](https://github.com/SVProgressHUD/SVProgressHUD/blob/master/LICENSE.txt). The success, error and info icons are made by [Freepik](http://www.freepik.com) from [Flaticon](http://www.flaticon.com) and are licensed under [Creative Commons BY 3.0](http://creativecommons.org/licenses/by/3.0/).
|
||||
|
||||
## Credits
|
||||
|
||||
`SVProgressHUD` is brought to you by [Sam Vermette](http://samvermette.com), [Tobias Tiemerding](http://tiemerding.com) and [contributors to the project](https://github.com/SVProgressHUD/SVProgressHUD/contributors). If you're using `SVProgressHUD` in your project, attribution would be very appreciated.
|
||||
17
Pods/SVProgressHUD/SVProgressHUD/SVIndefiniteAnimatedView.h
generated
Normal file
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// SVIndefiniteAnimatedView.h
|
||||
// SVProgressHUD, https://github.com/SVProgressHUD/SVProgressHUD
|
||||
//
|
||||
// Copyright (c) 2014-2018 Guillaume Campagna. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface SVIndefiniteAnimatedView : UIView
|
||||
|
||||
@property (nonatomic, assign) CGFloat strokeThickness;
|
||||
@property (nonatomic, assign) CGFloat radius;
|
||||
@property (nonatomic, strong) UIColor *strokeColor;
|
||||
|
||||
@end
|
||||
|
||||
137
Pods/SVProgressHUD/SVProgressHUD/SVIndefiniteAnimatedView.m
generated
Normal file
@@ -0,0 +1,137 @@
|
||||
//
|
||||
// SVIndefiniteAnimatedView.m
|
||||
// SVProgressHUD, https://github.com/SVProgressHUD/SVProgressHUD
|
||||
//
|
||||
// Copyright (c) 2014-2018 Guillaume Campagna. All rights reserved.
|
||||
//
|
||||
|
||||
#import "SVIndefiniteAnimatedView.h"
|
||||
#import "SVProgressHUD.h"
|
||||
|
||||
@interface SVIndefiniteAnimatedView ()
|
||||
|
||||
@property (nonatomic, strong) CAShapeLayer *indefiniteAnimatedLayer;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SVIndefiniteAnimatedView
|
||||
|
||||
- (void)willMoveToSuperview:(UIView*)newSuperview {
|
||||
if (newSuperview) {
|
||||
[self layoutAnimatedLayer];
|
||||
} else {
|
||||
[_indefiniteAnimatedLayer removeFromSuperlayer];
|
||||
_indefiniteAnimatedLayer = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)layoutAnimatedLayer {
|
||||
CALayer *layer = self.indefiniteAnimatedLayer;
|
||||
[self.layer addSublayer:layer];
|
||||
|
||||
CGFloat widthDiff = CGRectGetWidth(self.bounds) - CGRectGetWidth(layer.bounds);
|
||||
CGFloat heightDiff = CGRectGetHeight(self.bounds) - CGRectGetHeight(layer.bounds);
|
||||
layer.position = CGPointMake(CGRectGetWidth(self.bounds) - CGRectGetWidth(layer.bounds) / 2 - widthDiff / 2, CGRectGetHeight(self.bounds) - CGRectGetHeight(layer.bounds) / 2 - heightDiff / 2);
|
||||
}
|
||||
|
||||
- (CAShapeLayer*)indefiniteAnimatedLayer {
|
||||
if(!_indefiniteAnimatedLayer) {
|
||||
CGPoint arcCenter = CGPointMake(self.radius+self.strokeThickness/2+5, self.radius+self.strokeThickness/2+5);
|
||||
UIBezierPath* smoothedPath = [UIBezierPath bezierPathWithArcCenter:arcCenter radius:self.radius startAngle:(CGFloat) (M_PI*3/2) endAngle:(CGFloat) (M_PI/2+M_PI*5) clockwise:YES];
|
||||
|
||||
_indefiniteAnimatedLayer = [CAShapeLayer layer];
|
||||
_indefiniteAnimatedLayer.contentsScale = [[UIScreen mainScreen] scale];
|
||||
_indefiniteAnimatedLayer.frame = CGRectMake(0.0f, 0.0f, arcCenter.x*2, arcCenter.y*2);
|
||||
_indefiniteAnimatedLayer.fillColor = [UIColor clearColor].CGColor;
|
||||
_indefiniteAnimatedLayer.strokeColor = self.strokeColor.CGColor;
|
||||
_indefiniteAnimatedLayer.lineWidth = self.strokeThickness;
|
||||
_indefiniteAnimatedLayer.lineCap = kCALineCapRound;
|
||||
_indefiniteAnimatedLayer.lineJoin = kCALineJoinBevel;
|
||||
_indefiniteAnimatedLayer.path = smoothedPath.CGPath;
|
||||
|
||||
CALayer *maskLayer = [CALayer layer];
|
||||
|
||||
NSBundle *bundle = [NSBundle bundleForClass:[SVProgressHUD class]];
|
||||
NSURL *url = [bundle URLForResource:@"SVProgressHUD" withExtension:@"bundle"];
|
||||
NSBundle *imageBundle = [NSBundle bundleWithURL:url];
|
||||
|
||||
NSString *path = [imageBundle pathForResource:@"angle-mask" ofType:@"png"];
|
||||
|
||||
maskLayer.contents = (__bridge id)[[UIImage imageWithContentsOfFile:path] CGImage];
|
||||
maskLayer.frame = _indefiniteAnimatedLayer.bounds;
|
||||
_indefiniteAnimatedLayer.mask = maskLayer;
|
||||
|
||||
NSTimeInterval animationDuration = 1;
|
||||
CAMediaTimingFunction *linearCurve = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
|
||||
|
||||
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
|
||||
animation.fromValue = (id) 0;
|
||||
animation.toValue = @(M_PI*2);
|
||||
animation.duration = animationDuration;
|
||||
animation.timingFunction = linearCurve;
|
||||
animation.removedOnCompletion = NO;
|
||||
animation.repeatCount = INFINITY;
|
||||
animation.fillMode = kCAFillModeForwards;
|
||||
animation.autoreverses = NO;
|
||||
[_indefiniteAnimatedLayer.mask addAnimation:animation forKey:@"rotate"];
|
||||
|
||||
CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
|
||||
animationGroup.duration = animationDuration;
|
||||
animationGroup.repeatCount = INFINITY;
|
||||
animationGroup.removedOnCompletion = NO;
|
||||
animationGroup.timingFunction = linearCurve;
|
||||
|
||||
CABasicAnimation *strokeStartAnimation = [CABasicAnimation animationWithKeyPath:@"strokeStart"];
|
||||
strokeStartAnimation.fromValue = @0.015;
|
||||
strokeStartAnimation.toValue = @0.515;
|
||||
|
||||
CABasicAnimation *strokeEndAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
|
||||
strokeEndAnimation.fromValue = @0.485;
|
||||
strokeEndAnimation.toValue = @0.985;
|
||||
|
||||
animationGroup.animations = @[strokeStartAnimation, strokeEndAnimation];
|
||||
[_indefiniteAnimatedLayer addAnimation:animationGroup forKey:@"progress"];
|
||||
|
||||
}
|
||||
return _indefiniteAnimatedLayer;
|
||||
}
|
||||
|
||||
- (void)setFrame:(CGRect)frame {
|
||||
if(!CGRectEqualToRect(frame, super.frame)) {
|
||||
[super setFrame:frame];
|
||||
|
||||
if(self.superview) {
|
||||
[self layoutAnimatedLayer];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
- (void)setRadius:(CGFloat)radius {
|
||||
if(radius != _radius) {
|
||||
_radius = radius;
|
||||
|
||||
[_indefiniteAnimatedLayer removeFromSuperlayer];
|
||||
_indefiniteAnimatedLayer = nil;
|
||||
|
||||
if(self.superview) {
|
||||
[self layoutAnimatedLayer];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setStrokeColor:(UIColor*)strokeColor {
|
||||
_strokeColor = strokeColor;
|
||||
_indefiniteAnimatedLayer.strokeColor = strokeColor.CGColor;
|
||||
}
|
||||
|
||||
- (void)setStrokeThickness:(CGFloat)strokeThickness {
|
||||
_strokeThickness = strokeThickness;
|
||||
_indefiniteAnimatedLayer.lineWidth = _strokeThickness;
|
||||
}
|
||||
|
||||
- (CGSize)sizeThatFits:(CGSize)size {
|
||||
return CGSizeMake((self.radius+self.strokeThickness/2+5)*2, (self.radius+self.strokeThickness/2+5)*2);
|
||||
}
|
||||
|
||||
@end
|
||||
17
Pods/SVProgressHUD/SVProgressHUD/SVProgressAnimatedView.h
generated
Normal file
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// SVProgressAnimatedView.h
|
||||
// SVProgressHUD, https://github.com/SVProgressHUD/SVProgressHUD
|
||||
//
|
||||
// Copyright (c) 2017-2018 Tobias Tiemerding. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface SVProgressAnimatedView : UIView
|
||||
|
||||
@property (nonatomic, assign) CGFloat radius;
|
||||
@property (nonatomic, assign) CGFloat strokeThickness;
|
||||
@property (nonatomic, strong) UIColor *strokeColor;
|
||||
@property (nonatomic, assign) CGFloat strokeEnd;
|
||||
|
||||
@end
|
||||
96
Pods/SVProgressHUD/SVProgressHUD/SVProgressAnimatedView.m
generated
Normal file
@@ -0,0 +1,96 @@
|
||||
//
|
||||
// SVProgressAnimatedView.m
|
||||
// SVProgressHUD, https://github.com/SVProgressHUD/SVProgressHUD
|
||||
//
|
||||
// Copyright (c) 2017-2018 Tobias Tiemerding. All rights reserved.
|
||||
//
|
||||
|
||||
#import "SVProgressAnimatedView.h"
|
||||
|
||||
@interface SVProgressAnimatedView ()
|
||||
|
||||
@property (nonatomic, strong) CAShapeLayer *ringAnimatedLayer;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SVProgressAnimatedView
|
||||
|
||||
- (void)willMoveToSuperview:(UIView*)newSuperview {
|
||||
if (newSuperview) {
|
||||
[self layoutAnimatedLayer];
|
||||
} else {
|
||||
[_ringAnimatedLayer removeFromSuperlayer];
|
||||
_ringAnimatedLayer = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)layoutAnimatedLayer {
|
||||
CALayer *layer = self.ringAnimatedLayer;
|
||||
[self.layer addSublayer:layer];
|
||||
|
||||
CGFloat widthDiff = CGRectGetWidth(self.bounds) - CGRectGetWidth(layer.bounds);
|
||||
CGFloat heightDiff = CGRectGetHeight(self.bounds) - CGRectGetHeight(layer.bounds);
|
||||
layer.position = CGPointMake(CGRectGetWidth(self.bounds) - CGRectGetWidth(layer.bounds) / 2 - widthDiff / 2, CGRectGetHeight(self.bounds) - CGRectGetHeight(layer.bounds) / 2 - heightDiff / 2);
|
||||
}
|
||||
|
||||
- (CAShapeLayer*)ringAnimatedLayer {
|
||||
if(!_ringAnimatedLayer) {
|
||||
CGPoint arcCenter = CGPointMake(self.radius+self.strokeThickness/2+5, self.radius+self.strokeThickness/2+5);
|
||||
UIBezierPath* smoothedPath = [UIBezierPath bezierPathWithArcCenter:arcCenter radius:self.radius startAngle:(CGFloat)-M_PI_2 endAngle:(CGFloat) (M_PI + M_PI_2) clockwise:YES];
|
||||
|
||||
_ringAnimatedLayer = [CAShapeLayer layer];
|
||||
_ringAnimatedLayer.contentsScale = [[UIScreen mainScreen] scale];
|
||||
_ringAnimatedLayer.frame = CGRectMake(0.0f, 0.0f, arcCenter.x*2, arcCenter.y*2);
|
||||
_ringAnimatedLayer.fillColor = [UIColor clearColor].CGColor;
|
||||
_ringAnimatedLayer.strokeColor = self.strokeColor.CGColor;
|
||||
_ringAnimatedLayer.lineWidth = self.strokeThickness;
|
||||
_ringAnimatedLayer.lineCap = kCALineCapRound;
|
||||
_ringAnimatedLayer.lineJoin = kCALineJoinBevel;
|
||||
_ringAnimatedLayer.path = smoothedPath.CGPath;
|
||||
}
|
||||
return _ringAnimatedLayer;
|
||||
}
|
||||
|
||||
- (void)setFrame:(CGRect)frame {
|
||||
if(!CGRectEqualToRect(frame, super.frame)) {
|
||||
[super setFrame:frame];
|
||||
|
||||
if(self.superview) {
|
||||
[self layoutAnimatedLayer];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setRadius:(CGFloat)radius {
|
||||
if(radius != _radius) {
|
||||
_radius = radius;
|
||||
|
||||
[_ringAnimatedLayer removeFromSuperlayer];
|
||||
_ringAnimatedLayer = nil;
|
||||
|
||||
if(self.superview) {
|
||||
[self layoutAnimatedLayer];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setStrokeColor:(UIColor*)strokeColor {
|
||||
_strokeColor = strokeColor;
|
||||
_ringAnimatedLayer.strokeColor = strokeColor.CGColor;
|
||||
}
|
||||
|
||||
- (void)setStrokeThickness:(CGFloat)strokeThickness {
|
||||
_strokeThickness = strokeThickness;
|
||||
_ringAnimatedLayer.lineWidth = _strokeThickness;
|
||||
}
|
||||
|
||||
- (void)setStrokeEnd:(CGFloat)strokeEnd {
|
||||
_strokeEnd = strokeEnd;
|
||||
_ringAnimatedLayer.strokeEnd = _strokeEnd;
|
||||
}
|
||||
|
||||
- (CGSize)sizeThatFits:(CGSize)size {
|
||||
return CGSizeMake((self.radius+self.strokeThickness/2+5)*2, (self.radius+self.strokeThickness/2+5)*2);
|
||||
}
|
||||
|
||||
@end
|
||||
BIN
Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/angle-mask.png
generated
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/angle-mask@2x.png
generated
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/angle-mask@3x.png
generated
Normal file
|
After Width: | Height: | Size: 8.1 KiB |
BIN
Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/error.png
generated
Normal file
|
After Width: | Height: | Size: 184 B |
BIN
Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/error@2x.png
generated
Normal file
|
After Width: | Height: | Size: 306 B |
BIN
Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/error@3x.png
generated
Normal file
|
After Width: | Height: | Size: 398 B |
BIN
Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/info.png
generated
Normal file
|
After Width: | Height: | Size: 365 B |
BIN
Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/info@2x.png
generated
Normal file
|
After Width: | Height: | Size: 816 B |
BIN
Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/info@3x.png
generated
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/success.png
generated
Normal file
|
After Width: | Height: | Size: 262 B |
BIN
Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/success@2x.png
generated
Normal file
|
After Width: | Height: | Size: 462 B |
BIN
Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.bundle/success@3x.png
generated
Normal file
|
After Width: | Height: | Size: 714 B |
147
Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.h
generated
Normal file
@@ -0,0 +1,147 @@
|
||||
//
|
||||
// SVProgressHUD.h
|
||||
// SVProgressHUD, https://github.com/SVProgressHUD/SVProgressHUD
|
||||
//
|
||||
// Copyright (c) 2011-2018 Sam Vermette and contributors. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <AvailabilityMacros.h>
|
||||
|
||||
extern NSString * _Nonnull const SVProgressHUDDidReceiveTouchEventNotification;
|
||||
extern NSString * _Nonnull const SVProgressHUDDidTouchDownInsideNotification;
|
||||
extern NSString * _Nonnull const SVProgressHUDWillDisappearNotification;
|
||||
extern NSString * _Nonnull const SVProgressHUDDidDisappearNotification;
|
||||
extern NSString * _Nonnull const SVProgressHUDWillAppearNotification;
|
||||
extern NSString * _Nonnull const SVProgressHUDDidAppearNotification;
|
||||
|
||||
extern NSString * _Nonnull const SVProgressHUDStatusUserInfoKey;
|
||||
|
||||
typedef NS_ENUM(NSInteger, SVProgressHUDStyle) {
|
||||
SVProgressHUDStyleLight, // default style, white HUD with black text, HUD background will be blurred
|
||||
SVProgressHUDStyleDark, // black HUD and white text, HUD background will be blurred
|
||||
SVProgressHUDStyleCustom // uses the fore- and background color properties
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSUInteger, SVProgressHUDMaskType) {
|
||||
SVProgressHUDMaskTypeNone = 1, // default mask type, allow user interactions while HUD is displayed
|
||||
SVProgressHUDMaskTypeClear, // don't allow user interactions with background objects
|
||||
SVProgressHUDMaskTypeBlack, // don't allow user interactions with background objects and dim the UI in the back of the HUD (as seen in iOS 7 and above)
|
||||
SVProgressHUDMaskTypeGradient, // don't allow user interactions with background objects and dim the UI with a a-la UIAlertView background gradient (as seen in iOS 6)
|
||||
SVProgressHUDMaskTypeCustom // don't allow user interactions with background objects and dim the UI in the back of the HUD with a custom color
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSUInteger, SVProgressHUDAnimationType) {
|
||||
SVProgressHUDAnimationTypeFlat, // default animation type, custom flat animation (indefinite animated ring)
|
||||
SVProgressHUDAnimationTypeNative // iOS native UIActivityIndicatorView
|
||||
};
|
||||
|
||||
typedef void (^SVProgressHUDShowCompletion)(void);
|
||||
typedef void (^SVProgressHUDDismissCompletion)(void);
|
||||
|
||||
@interface SVProgressHUD : UIView
|
||||
|
||||
#pragma mark - Customization
|
||||
|
||||
@property (assign, nonatomic) SVProgressHUDStyle defaultStyle UI_APPEARANCE_SELECTOR; // default is SVProgressHUDStyleLight
|
||||
@property (assign, nonatomic) SVProgressHUDMaskType defaultMaskType UI_APPEARANCE_SELECTOR; // default is SVProgressHUDMaskTypeNone
|
||||
@property (assign, nonatomic) SVProgressHUDAnimationType defaultAnimationType UI_APPEARANCE_SELECTOR; // default is SVProgressHUDAnimationTypeFlat
|
||||
@property (strong, nonatomic, nullable) UIView *containerView; // if nil then use default window level
|
||||
@property (assign, nonatomic) CGSize minimumSize UI_APPEARANCE_SELECTOR; // default is CGSizeZero, can be used to avoid resizing for a larger message
|
||||
@property (assign, nonatomic) CGFloat ringThickness UI_APPEARANCE_SELECTOR; // default is 2 pt
|
||||
@property (assign, nonatomic) CGFloat ringRadius UI_APPEARANCE_SELECTOR; // default is 18 pt
|
||||
@property (assign, nonatomic) CGFloat ringNoTextRadius UI_APPEARANCE_SELECTOR; // default is 24 pt
|
||||
@property (assign, nonatomic) CGFloat cornerRadius UI_APPEARANCE_SELECTOR; // default is 14 pt
|
||||
@property (strong, nonatomic, nonnull) UIFont *font UI_APPEARANCE_SELECTOR; // default is [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline]
|
||||
@property (strong, nonatomic, nonnull) UIColor *backgroundColor UI_APPEARANCE_SELECTOR; // default is [UIColor whiteColor]
|
||||
@property (strong, nonatomic, nonnull) UIColor *foregroundColor UI_APPEARANCE_SELECTOR; // default is [UIColor blackColor]
|
||||
@property (strong, nonatomic, nonnull) UIColor *backgroundLayerColor UI_APPEARANCE_SELECTOR;// default is [UIColor colorWithWhite:0 alpha:0.4]
|
||||
@property (assign, nonatomic) CGSize imageViewSize UI_APPEARANCE_SELECTOR; // default is 28x28 pt
|
||||
@property (assign, nonatomic) BOOL shouldTintImages UI_APPEARANCE_SELECTOR; // default is YES
|
||||
@property (strong, nonatomic, nonnull) UIImage *infoImage UI_APPEARANCE_SELECTOR; // default is the bundled info image provided by Freepik
|
||||
@property (strong, nonatomic, nonnull) UIImage *successImage UI_APPEARANCE_SELECTOR; // default is the bundled success image provided by Freepik
|
||||
@property (strong, nonatomic, nonnull) UIImage *errorImage UI_APPEARANCE_SELECTOR; // default is the bundled error image provided by Freepik
|
||||
@property (strong, nonatomic, nonnull) UIView *viewForExtension UI_APPEARANCE_SELECTOR; // default is nil, only used if #define SV_APP_EXTENSIONS is set
|
||||
@property (assign, nonatomic) NSTimeInterval graceTimeInterval; // default is 0 seconds
|
||||
@property (assign, nonatomic) NSTimeInterval minimumDismissTimeInterval; // default is 5.0 seconds
|
||||
@property (assign, nonatomic) NSTimeInterval maximumDismissTimeInterval; // default is CGFLOAT_MAX
|
||||
|
||||
@property (assign, nonatomic) UIOffset offsetFromCenter UI_APPEARANCE_SELECTOR; // default is 0, 0
|
||||
|
||||
@property (assign, nonatomic) NSTimeInterval fadeInAnimationDuration UI_APPEARANCE_SELECTOR; // default is 0.15
|
||||
@property (assign, nonatomic) NSTimeInterval fadeOutAnimationDuration UI_APPEARANCE_SELECTOR; // default is 0.15
|
||||
|
||||
@property (assign, nonatomic) UIWindowLevel maxSupportedWindowLevel; // default is UIWindowLevelNormal
|
||||
|
||||
@property (assign, nonatomic) BOOL hapticsEnabled; // default is NO
|
||||
|
||||
+ (void)setDefaultStyle:(SVProgressHUDStyle)style; // default is SVProgressHUDStyleLight
|
||||
+ (void)setDefaultMaskType:(SVProgressHUDMaskType)maskType; // default is SVProgressHUDMaskTypeNone
|
||||
+ (void)setDefaultAnimationType:(SVProgressHUDAnimationType)type; // default is SVProgressHUDAnimationTypeFlat
|
||||
+ (void)setContainerView:(nullable UIView*)containerView; // default is window level
|
||||
+ (void)setMinimumSize:(CGSize)minimumSize; // default is CGSizeZero, can be used to avoid resizing for a larger message
|
||||
+ (void)setRingThickness:(CGFloat)ringThickness; // default is 2 pt
|
||||
+ (void)setRingRadius:(CGFloat)radius; // default is 18 pt
|
||||
+ (void)setRingNoTextRadius:(CGFloat)radius; // default is 24 pt
|
||||
+ (void)setCornerRadius:(CGFloat)cornerRadius; // default is 14 pt
|
||||
+ (void)setBorderColor:(nonnull UIColor*)color; // default is nil
|
||||
+ (void)setBorderWidth:(CGFloat)width; // default is 0
|
||||
+ (void)setFont:(nonnull UIFont*)font; // default is [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline]
|
||||
+ (void)setForegroundColor:(nonnull UIColor*)color; // default is [UIColor blackColor], only used for SVProgressHUDStyleCustom
|
||||
+ (void)setBackgroundColor:(nonnull UIColor*)color; // default is [UIColor whiteColor], only used for SVProgressHUDStyleCustom
|
||||
+ (void)setBackgroundLayerColor:(nonnull UIColor*)color; // default is [UIColor colorWithWhite:0 alpha:0.5], only used for SVProgressHUDMaskTypeCustom
|
||||
+ (void)setImageViewSize:(CGSize)size; // default is 28x28 pt
|
||||
+ (void)setShouldTintImages:(BOOL)shouldTintImages; // default is YES
|
||||
+ (void)setInfoImage:(nonnull UIImage*)image; // default is the bundled info image provided by Freepik
|
||||
+ (void)setSuccessImage:(nonnull UIImage*)image; // default is the bundled success image provided by Freepik
|
||||
+ (void)setErrorImage:(nonnull UIImage*)image; // default is the bundled error image provided by Freepik
|
||||
+ (void)setViewForExtension:(nonnull UIView*)view; // default is nil, only used if #define SV_APP_EXTENSIONS is set
|
||||
+ (void)setGraceTimeInterval:(NSTimeInterval)interval; // default is 0 seconds
|
||||
+ (void)setMinimumDismissTimeInterval:(NSTimeInterval)interval; // default is 5.0 seconds
|
||||
+ (void)setMaximumDismissTimeInterval:(NSTimeInterval)interval; // default is infinite
|
||||
+ (void)setFadeInAnimationDuration:(NSTimeInterval)duration; // default is 0.15 seconds
|
||||
+ (void)setFadeOutAnimationDuration:(NSTimeInterval)duration; // default is 0.15 seconds
|
||||
+ (void)setMaxSupportedWindowLevel:(UIWindowLevel)windowLevel; // default is UIWindowLevelNormal
|
||||
+ (void)setHapticsEnabled:(BOOL)hapticsEnabled; // default is NO
|
||||
|
||||
#pragma mark - Show Methods
|
||||
|
||||
+ (void)show;
|
||||
+ (void)showWithMaskType:(SVProgressHUDMaskType)maskType __attribute__((deprecated("Use show and setDefaultMaskType: instead.")));
|
||||
+ (void)showWithStatus:(nullable NSString*)status;
|
||||
+ (void)showWithStatus:(nullable NSString*)status maskType:(SVProgressHUDMaskType)maskType __attribute__((deprecated("Use showWithStatus: and setDefaultMaskType: instead.")));
|
||||
|
||||
+ (void)showProgress:(float)progress;
|
||||
+ (void)showProgress:(float)progress maskType:(SVProgressHUDMaskType)maskType __attribute__((deprecated("Use showProgress: and setDefaultMaskType: instead.")));
|
||||
+ (void)showProgress:(float)progress status:(nullable NSString*)status;
|
||||
+ (void)showProgress:(float)progress status:(nullable NSString*)status maskType:(SVProgressHUDMaskType)maskType __attribute__((deprecated("Use showProgress:status: and setDefaultMaskType: instead.")));
|
||||
|
||||
+ (void)setStatus:(nullable NSString*)status; // change the HUD loading status while it's showing
|
||||
|
||||
// stops the activity indicator, shows a glyph + status, and dismisses the HUD a little bit later
|
||||
+ (void)showInfoWithStatus:(nullable NSString*)status;
|
||||
+ (void)showInfoWithStatus:(nullable NSString*)status maskType:(SVProgressHUDMaskType)maskType __attribute__((deprecated("Use showInfoWithStatus: and setDefaultMaskType: instead.")));
|
||||
+ (void)showSuccessWithStatus:(nullable NSString*)status;
|
||||
+ (void)showSuccessWithStatus:(nullable NSString*)status maskType:(SVProgressHUDMaskType)maskType __attribute__((deprecated("Use showSuccessWithStatus: and setDefaultMaskType: instead.")));
|
||||
+ (void)showErrorWithStatus:(nullable NSString*)status;
|
||||
+ (void)showErrorWithStatus:(nullable NSString*)status maskType:(SVProgressHUDMaskType)maskType __attribute__((deprecated("Use showErrorWithStatus: and setDefaultMaskType: instead.")));
|
||||
|
||||
// shows a image + status, use white PNGs with the imageViewSize (default is 28x28 pt)
|
||||
+ (void)showImage:(nonnull UIImage*)image status:(nullable NSString*)status;
|
||||
+ (void)showImage:(nonnull UIImage*)image status:(nullable NSString*)status maskType:(SVProgressHUDMaskType)maskType __attribute__((deprecated("Use showImage:status: and setDefaultMaskType: instead.")));
|
||||
|
||||
+ (void)setOffsetFromCenter:(UIOffset)offset;
|
||||
+ (void)resetOffsetFromCenter;
|
||||
|
||||
+ (void)popActivity; // decrease activity count, if activity count == 0 the HUD is dismissed
|
||||
+ (void)dismiss;
|
||||
+ (void)dismissWithCompletion:(nullable SVProgressHUDDismissCompletion)completion;
|
||||
+ (void)dismissWithDelay:(NSTimeInterval)delay;
|
||||
+ (void)dismissWithDelay:(NSTimeInterval)delay completion:(nullable SVProgressHUDDismissCompletion)completion;
|
||||
|
||||
+ (BOOL)isVisible;
|
||||
|
||||
+ (NSTimeInterval)displayDurationForString:(nullable NSString*)string;
|
||||
|
||||
@end
|
||||
|
||||
1509
Pods/SVProgressHUD/SVProgressHUD/SVProgressHUD.m
generated
Normal file
@@ -0,0 +1,1509 @@
|
||||
//
|
||||
// SVProgressHUD.h
|
||||
// SVProgressHUD, https://github.com/SVProgressHUD/SVProgressHUD
|
||||
//
|
||||
// Copyright (c) 2011-2018 Sam Vermette and contributors. All rights reserved.
|
||||
//
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error SVProgressHUD is ARC only. Either turn on ARC for the project or use -fobjc-arc flag
|
||||
#endif
|
||||
|
||||
#import "SVProgressHUD.h"
|
||||
#import "SVIndefiniteAnimatedView.h"
|
||||
#import "SVProgressAnimatedView.h"
|
||||
#import "SVRadialGradientLayer.h"
|
||||
|
||||
NSString * const SVProgressHUDDidReceiveTouchEventNotification = @"SVProgressHUDDidReceiveTouchEventNotification";
|
||||
NSString * const SVProgressHUDDidTouchDownInsideNotification = @"SVProgressHUDDidTouchDownInsideNotification";
|
||||
NSString * const SVProgressHUDWillDisappearNotification = @"SVProgressHUDWillDisappearNotification";
|
||||
NSString * const SVProgressHUDDidDisappearNotification = @"SVProgressHUDDidDisappearNotification";
|
||||
NSString * const SVProgressHUDWillAppearNotification = @"SVProgressHUDWillAppearNotification";
|
||||
NSString * const SVProgressHUDDidAppearNotification = @"SVProgressHUDDidAppearNotification";
|
||||
|
||||
NSString * const SVProgressHUDStatusUserInfoKey = @"SVProgressHUDStatusUserInfoKey";
|
||||
|
||||
static const CGFloat SVProgressHUDParallaxDepthPoints = 10.0f;
|
||||
static const CGFloat SVProgressHUDUndefinedProgress = -1;
|
||||
static const CGFloat SVProgressHUDDefaultAnimationDuration = 0.15f;
|
||||
static const CGFloat SVProgressHUDVerticalSpacing = 12.0f;
|
||||
static const CGFloat SVProgressHUDHorizontalSpacing = 12.0f;
|
||||
static const CGFloat SVProgressHUDLabelSpacing = 8.0f;
|
||||
|
||||
|
||||
@interface SVProgressHUD ()
|
||||
|
||||
@property (nonatomic, strong) NSTimer *graceTimer;
|
||||
@property (nonatomic, strong) NSTimer *fadeOutTimer;
|
||||
|
||||
@property (nonatomic, strong) UIControl *controlView;
|
||||
@property (nonatomic, strong) UIView *backgroundView;
|
||||
@property (nonatomic, strong) SVRadialGradientLayer *backgroundRadialGradientLayer;
|
||||
@property (nonatomic, strong) UIVisualEffectView *hudView;
|
||||
@property (nonatomic, strong) UILabel *statusLabel;
|
||||
@property (nonatomic, strong) UIImageView *imageView;
|
||||
|
||||
@property (nonatomic, strong) UIView *indefiniteAnimatedView;
|
||||
@property (nonatomic, strong) SVProgressAnimatedView *ringView;
|
||||
@property (nonatomic, strong) SVProgressAnimatedView *backgroundRingView;
|
||||
|
||||
@property (nonatomic, readwrite) CGFloat progress;
|
||||
@property (nonatomic, readwrite) NSUInteger activityCount;
|
||||
|
||||
@property (nonatomic, readonly) CGFloat visibleKeyboardHeight;
|
||||
@property (nonatomic, readonly) UIWindow *frontWindow;
|
||||
|
||||
#if TARGET_OS_IOS && __IPHONE_OS_VERSION_MAX_ALLOWED >= 100000
|
||||
@property (nonatomic, strong) UINotificationFeedbackGenerator *hapticGenerator NS_AVAILABLE_IOS(10_0);
|
||||
#endif
|
||||
|
||||
@end
|
||||
|
||||
@implementation SVProgressHUD {
|
||||
BOOL _isInitializing;
|
||||
}
|
||||
|
||||
+ (SVProgressHUD*)sharedView {
|
||||
static dispatch_once_t once;
|
||||
|
||||
static SVProgressHUD *sharedView;
|
||||
#if !defined(SV_APP_EXTENSIONS)
|
||||
dispatch_once(&once, ^{ sharedView = [[self alloc] initWithFrame:[[[UIApplication sharedApplication] delegate] window].bounds]; });
|
||||
#else
|
||||
dispatch_once(&once, ^{ sharedView = [[self alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; });
|
||||
#endif
|
||||
return sharedView;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Setters
|
||||
|
||||
+ (void)setStatus:(NSString*)status {
|
||||
[[self sharedView] setStatus:status];
|
||||
}
|
||||
|
||||
+ (void)setDefaultStyle:(SVProgressHUDStyle)style {
|
||||
[self sharedView].defaultStyle = style;
|
||||
}
|
||||
|
||||
+ (void)setDefaultMaskType:(SVProgressHUDMaskType)maskType {
|
||||
[self sharedView].defaultMaskType = maskType;
|
||||
}
|
||||
|
||||
+ (void)setDefaultAnimationType:(SVProgressHUDAnimationType)type {
|
||||
[self sharedView].defaultAnimationType = type;
|
||||
}
|
||||
|
||||
+ (void)setContainerView:(nullable UIView*)containerView {
|
||||
[self sharedView].containerView = containerView;
|
||||
}
|
||||
|
||||
+ (void)setMinimumSize:(CGSize)minimumSize {
|
||||
[self sharedView].minimumSize = minimumSize;
|
||||
}
|
||||
|
||||
+ (void)setRingThickness:(CGFloat)ringThickness {
|
||||
[self sharedView].ringThickness = ringThickness;
|
||||
}
|
||||
|
||||
+ (void)setRingRadius:(CGFloat)radius {
|
||||
[self sharedView].ringRadius = radius;
|
||||
}
|
||||
|
||||
+ (void)setRingNoTextRadius:(CGFloat)radius {
|
||||
[self sharedView].ringNoTextRadius = radius;
|
||||
}
|
||||
|
||||
+ (void)setCornerRadius:(CGFloat)cornerRadius {
|
||||
[self sharedView].cornerRadius = cornerRadius;
|
||||
}
|
||||
|
||||
+ (void)setBorderColor:(nonnull UIColor*)color {
|
||||
[self sharedView].hudView.layer.borderColor = color.CGColor;
|
||||
}
|
||||
|
||||
+ (void)setBorderWidth:(CGFloat)width {
|
||||
[self sharedView].hudView.layer.borderWidth = width;
|
||||
}
|
||||
|
||||
+ (void)setFont:(UIFont*)font {
|
||||
[self sharedView].font = font;
|
||||
}
|
||||
|
||||
+ (void)setForegroundColor:(UIColor*)color {
|
||||
[self sharedView].foregroundColor = color;
|
||||
[self setDefaultStyle:SVProgressHUDStyleCustom];
|
||||
}
|
||||
|
||||
+ (void)setBackgroundColor:(UIColor*)color {
|
||||
[self sharedView].backgroundColor = color;
|
||||
[self setDefaultStyle:SVProgressHUDStyleCustom];
|
||||
}
|
||||
|
||||
+ (void)setBackgroundLayerColor:(UIColor*)color {
|
||||
[self sharedView].backgroundLayerColor = color;
|
||||
}
|
||||
|
||||
+ (void)setImageViewSize:(CGSize)size {
|
||||
[self sharedView].imageViewSize = size;
|
||||
}
|
||||
|
||||
+ (void)setShouldTintImages:(BOOL)shouldTintImages {
|
||||
[self sharedView].shouldTintImages = shouldTintImages;
|
||||
}
|
||||
|
||||
+ (void)setInfoImage:(UIImage*)image {
|
||||
[self sharedView].infoImage = image;
|
||||
}
|
||||
|
||||
+ (void)setSuccessImage:(UIImage*)image {
|
||||
[self sharedView].successImage = image;
|
||||
}
|
||||
|
||||
+ (void)setErrorImage:(UIImage*)image {
|
||||
[self sharedView].errorImage = image;
|
||||
}
|
||||
|
||||
+ (void)setViewForExtension:(UIView*)view {
|
||||
[self sharedView].viewForExtension = view;
|
||||
}
|
||||
|
||||
+ (void)setGraceTimeInterval:(NSTimeInterval)interval {
|
||||
[self sharedView].graceTimeInterval = interval;
|
||||
}
|
||||
|
||||
+ (void)setMinimumDismissTimeInterval:(NSTimeInterval)interval {
|
||||
[self sharedView].minimumDismissTimeInterval = interval;
|
||||
}
|
||||
|
||||
+ (void)setMaximumDismissTimeInterval:(NSTimeInterval)interval {
|
||||
[self sharedView].maximumDismissTimeInterval = interval;
|
||||
}
|
||||
|
||||
+ (void)setFadeInAnimationDuration:(NSTimeInterval)duration {
|
||||
[self sharedView].fadeInAnimationDuration = duration;
|
||||
}
|
||||
|
||||
+ (void)setFadeOutAnimationDuration:(NSTimeInterval)duration {
|
||||
[self sharedView].fadeOutAnimationDuration = duration;
|
||||
}
|
||||
|
||||
+ (void)setMaxSupportedWindowLevel:(UIWindowLevel)windowLevel {
|
||||
[self sharedView].maxSupportedWindowLevel = windowLevel;
|
||||
}
|
||||
|
||||
+ (void)setHapticsEnabled:(BOOL)hapticsEnabled {
|
||||
[self sharedView].hapticsEnabled = hapticsEnabled;
|
||||
}
|
||||
|
||||
#pragma mark - Show Methods
|
||||
|
||||
+ (void)show {
|
||||
[self showWithStatus:nil];
|
||||
}
|
||||
|
||||
+ (void)showWithMaskType:(SVProgressHUDMaskType)maskType {
|
||||
SVProgressHUDMaskType existingMaskType = [self sharedView].defaultMaskType;
|
||||
[self setDefaultMaskType:maskType];
|
||||
[self show];
|
||||
[self setDefaultMaskType:existingMaskType];
|
||||
}
|
||||
|
||||
+ (void)showWithStatus:(NSString*)status {
|
||||
[self showProgress:SVProgressHUDUndefinedProgress status:status];
|
||||
}
|
||||
|
||||
+ (void)showWithStatus:(NSString*)status maskType:(SVProgressHUDMaskType)maskType {
|
||||
SVProgressHUDMaskType existingMaskType = [self sharedView].defaultMaskType;
|
||||
[self setDefaultMaskType:maskType];
|
||||
[self showWithStatus:status];
|
||||
[self setDefaultMaskType:existingMaskType];
|
||||
}
|
||||
|
||||
+ (void)showProgress:(float)progress {
|
||||
[self showProgress:progress status:nil];
|
||||
}
|
||||
|
||||
+ (void)showProgress:(float)progress maskType:(SVProgressHUDMaskType)maskType {
|
||||
SVProgressHUDMaskType existingMaskType = [self sharedView].defaultMaskType;
|
||||
[self setDefaultMaskType:maskType];
|
||||
[self showProgress:progress];
|
||||
[self setDefaultMaskType:existingMaskType];
|
||||
}
|
||||
|
||||
+ (void)showProgress:(float)progress status:(NSString*)status {
|
||||
[[self sharedView] showProgress:progress status:status];
|
||||
}
|
||||
|
||||
+ (void)showProgress:(float)progress status:(NSString*)status maskType:(SVProgressHUDMaskType)maskType {
|
||||
SVProgressHUDMaskType existingMaskType = [self sharedView].defaultMaskType;
|
||||
[self setDefaultMaskType:maskType];
|
||||
[self showProgress:progress status:status];
|
||||
[self setDefaultMaskType:existingMaskType];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Show, then automatically dismiss methods
|
||||
|
||||
+ (void)showInfoWithStatus:(NSString*)status {
|
||||
[self showImage:[self sharedView].infoImage status:status];
|
||||
|
||||
#if TARGET_OS_IOS && __IPHONE_OS_VERSION_MAX_ALLOWED >= 100000
|
||||
if (@available(iOS 10.0, *)) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[[self sharedView].hapticGenerator notificationOccurred:UINotificationFeedbackTypeWarning];
|
||||
});
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
+ (void)showInfoWithStatus:(NSString*)status maskType:(SVProgressHUDMaskType)maskType {
|
||||
SVProgressHUDMaskType existingMaskType = [self sharedView].defaultMaskType;
|
||||
[self setDefaultMaskType:maskType];
|
||||
[self showInfoWithStatus:status];
|
||||
[self setDefaultMaskType:existingMaskType];
|
||||
}
|
||||
|
||||
+ (void)showSuccessWithStatus:(NSString*)status {
|
||||
[self showImage:[self sharedView].successImage status:status];
|
||||
|
||||
#if TARGET_OS_IOS && __IPHONE_OS_VERSION_MAX_ALLOWED >= 100000
|
||||
if (@available(iOS 10, *)) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[[self sharedView].hapticGenerator notificationOccurred:UINotificationFeedbackTypeSuccess];
|
||||
});
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
+ (void)showSuccessWithStatus:(NSString*)status maskType:(SVProgressHUDMaskType)maskType {
|
||||
SVProgressHUDMaskType existingMaskType = [self sharedView].defaultMaskType;
|
||||
[self setDefaultMaskType:maskType];
|
||||
[self showSuccessWithStatus:status];
|
||||
[self setDefaultMaskType:existingMaskType];
|
||||
|
||||
#if TARGET_OS_IOS && __IPHONE_OS_VERSION_MAX_ALLOWED >= 100000
|
||||
if (@available(iOS 10.0, *)) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[[self sharedView].hapticGenerator notificationOccurred:UINotificationFeedbackTypeSuccess];
|
||||
});
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
+ (void)showErrorWithStatus:(NSString*)status {
|
||||
[self showImage:[self sharedView].errorImage status:status];
|
||||
|
||||
#if TARGET_OS_IOS && __IPHONE_OS_VERSION_MAX_ALLOWED >= 100000
|
||||
if (@available(iOS 10.0, *)) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[[self sharedView].hapticGenerator notificationOccurred:UINotificationFeedbackTypeError];
|
||||
});
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
+ (void)showErrorWithStatus:(NSString*)status maskType:(SVProgressHUDMaskType)maskType {
|
||||
SVProgressHUDMaskType existingMaskType = [self sharedView].defaultMaskType;
|
||||
[self setDefaultMaskType:maskType];
|
||||
[self showErrorWithStatus:status];
|
||||
[self setDefaultMaskType:existingMaskType];
|
||||
|
||||
#if TARGET_OS_IOS && __IPHONE_OS_VERSION_MAX_ALLOWED >= 100000
|
||||
if (@available(iOS 10.0, *)) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[[self sharedView].hapticGenerator notificationOccurred:UINotificationFeedbackTypeError];
|
||||
});
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
+ (void)showImage:(UIImage*)image status:(NSString*)status {
|
||||
NSTimeInterval displayInterval = [self displayDurationForString:status];
|
||||
[[self sharedView] showImage:image status:status duration:displayInterval];
|
||||
}
|
||||
|
||||
+ (void)showImage:(UIImage*)image status:(NSString*)status maskType:(SVProgressHUDMaskType)maskType {
|
||||
SVProgressHUDMaskType existingMaskType = [self sharedView].defaultMaskType;
|
||||
[self setDefaultMaskType:maskType];
|
||||
[self showImage:image status:status];
|
||||
[self setDefaultMaskType:existingMaskType];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Dismiss Methods
|
||||
|
||||
+ (void)popActivity {
|
||||
if([self sharedView].activityCount > 0) {
|
||||
[self sharedView].activityCount--;
|
||||
}
|
||||
if([self sharedView].activityCount == 0) {
|
||||
[[self sharedView] dismiss];
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)dismiss {
|
||||
[self dismissWithDelay:0.0 completion:nil];
|
||||
}
|
||||
|
||||
+ (void)dismissWithCompletion:(SVProgressHUDDismissCompletion)completion {
|
||||
[self dismissWithDelay:0.0 completion:completion];
|
||||
}
|
||||
|
||||
+ (void)dismissWithDelay:(NSTimeInterval)delay {
|
||||
[self dismissWithDelay:delay completion:nil];
|
||||
}
|
||||
|
||||
+ (void)dismissWithDelay:(NSTimeInterval)delay completion:(SVProgressHUDDismissCompletion)completion {
|
||||
[[self sharedView] dismissWithDelay:delay completion:completion];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Offset
|
||||
|
||||
+ (void)setOffsetFromCenter:(UIOffset)offset {
|
||||
[self sharedView].offsetFromCenter = offset;
|
||||
}
|
||||
|
||||
+ (void)resetOffsetFromCenter {
|
||||
[self setOffsetFromCenter:UIOffsetZero];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Instance Methods
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
if((self = [super initWithFrame:frame])) {
|
||||
_isInitializing = YES;
|
||||
|
||||
self.userInteractionEnabled = NO;
|
||||
self.activityCount = 0;
|
||||
|
||||
self.backgroundView.alpha = 0.0f;
|
||||
self.imageView.alpha = 0.0f;
|
||||
self.statusLabel.alpha = 0.0f;
|
||||
self.indefiniteAnimatedView.alpha = 0.0f;
|
||||
self.ringView.alpha = self.backgroundRingView.alpha = 0.0f;
|
||||
|
||||
|
||||
_backgroundColor = [UIColor whiteColor];
|
||||
_foregroundColor = [UIColor blackColor];
|
||||
_backgroundLayerColor = [UIColor colorWithWhite:0 alpha:0.4];
|
||||
|
||||
// Set default values
|
||||
_defaultMaskType = SVProgressHUDMaskTypeNone;
|
||||
_defaultStyle = SVProgressHUDStyleLight;
|
||||
_defaultAnimationType = SVProgressHUDAnimationTypeFlat;
|
||||
_minimumSize = CGSizeZero;
|
||||
_font = [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline];
|
||||
|
||||
_imageViewSize = CGSizeMake(28.0f, 28.0f);
|
||||
_shouldTintImages = YES;
|
||||
|
||||
NSBundle *bundle = [NSBundle bundleForClass:[SVProgressHUD class]];
|
||||
NSURL *url = [bundle URLForResource:@"SVProgressHUD" withExtension:@"bundle"];
|
||||
NSBundle *imageBundle = [NSBundle bundleWithURL:url];
|
||||
|
||||
_infoImage = [UIImage imageWithContentsOfFile:[imageBundle pathForResource:@"info" ofType:@"png"]];
|
||||
_successImage = [UIImage imageWithContentsOfFile:[imageBundle pathForResource:@"success" ofType:@"png"]];
|
||||
_errorImage = [UIImage imageWithContentsOfFile:[imageBundle pathForResource:@"error" ofType:@"png"]];
|
||||
|
||||
_ringThickness = 2.0f;
|
||||
_ringRadius = 18.0f;
|
||||
_ringNoTextRadius = 24.0f;
|
||||
|
||||
_cornerRadius = 14.0f;
|
||||
|
||||
_graceTimeInterval = 0.0f;
|
||||
_minimumDismissTimeInterval = 5.0;
|
||||
_maximumDismissTimeInterval = CGFLOAT_MAX;
|
||||
|
||||
_fadeInAnimationDuration = SVProgressHUDDefaultAnimationDuration;
|
||||
_fadeOutAnimationDuration = SVProgressHUDDefaultAnimationDuration;
|
||||
|
||||
_maxSupportedWindowLevel = UIWindowLevelNormal;
|
||||
|
||||
_hapticsEnabled = NO;
|
||||
|
||||
// Accessibility support
|
||||
self.accessibilityIdentifier = @"SVProgressHUD";
|
||||
self.isAccessibilityElement = YES;
|
||||
|
||||
_isInitializing = NO;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)updateHUDFrame {
|
||||
// Check if an image or progress ring is displayed
|
||||
BOOL imageUsed = (self.imageView.image) && !(self.imageView.hidden);
|
||||
BOOL progressUsed = self.imageView.hidden;
|
||||
|
||||
// Calculate size of string
|
||||
CGRect labelRect = CGRectZero;
|
||||
CGFloat labelHeight = 0.0f;
|
||||
CGFloat labelWidth = 0.0f;
|
||||
|
||||
if(self.statusLabel.text) {
|
||||
CGSize constraintSize = CGSizeMake(200.0f, 300.0f);
|
||||
labelRect = [self.statusLabel.text boundingRectWithSize:constraintSize
|
||||
options:(NSStringDrawingOptions)(NSStringDrawingUsesFontLeading | NSStringDrawingTruncatesLastVisibleLine | NSStringDrawingUsesLineFragmentOrigin)
|
||||
attributes:@{NSFontAttributeName: self.statusLabel.font}
|
||||
context:NULL];
|
||||
labelHeight = ceilf(CGRectGetHeight(labelRect));
|
||||
labelWidth = ceilf(CGRectGetWidth(labelRect));
|
||||
}
|
||||
|
||||
// Calculate hud size based on content
|
||||
// For the beginning use default values, these
|
||||
// might get update if string is too large etc.
|
||||
CGFloat hudWidth;
|
||||
CGFloat hudHeight;
|
||||
|
||||
CGFloat contentWidth = 0.0f;
|
||||
CGFloat contentHeight = 0.0f;
|
||||
|
||||
if(imageUsed || progressUsed) {
|
||||
contentWidth = CGRectGetWidth(imageUsed ? self.imageView.frame : self.indefiniteAnimatedView.frame);
|
||||
contentHeight = CGRectGetHeight(imageUsed ? self.imageView.frame : self.indefiniteAnimatedView.frame);
|
||||
}
|
||||
|
||||
// |-spacing-content-spacing-|
|
||||
hudWidth = SVProgressHUDHorizontalSpacing + MAX(labelWidth, contentWidth) + SVProgressHUDHorizontalSpacing;
|
||||
|
||||
// |-spacing-content-(labelSpacing-label-)spacing-|
|
||||
hudHeight = SVProgressHUDVerticalSpacing + labelHeight + contentHeight + SVProgressHUDVerticalSpacing;
|
||||
if(self.statusLabel.text && (imageUsed || progressUsed)){
|
||||
// Add spacing if both content and label are used
|
||||
hudHeight += SVProgressHUDLabelSpacing;
|
||||
}
|
||||
|
||||
// Update values on subviews
|
||||
self.hudView.bounds = CGRectMake(0.0f, 0.0f, MAX(self.minimumSize.width, hudWidth), MAX(self.minimumSize.height, hudHeight));
|
||||
|
||||
// Animate value update
|
||||
[CATransaction begin];
|
||||
[CATransaction setDisableActions:YES];
|
||||
|
||||
// Spinner and image view
|
||||
CGFloat centerY;
|
||||
if(self.statusLabel.text) {
|
||||
CGFloat yOffset = MAX(SVProgressHUDVerticalSpacing, (self.minimumSize.height - contentHeight - SVProgressHUDLabelSpacing - labelHeight) / 2.0f);
|
||||
centerY = yOffset + contentHeight / 2.0f;
|
||||
} else {
|
||||
centerY = CGRectGetMidY(self.hudView.bounds);
|
||||
}
|
||||
self.indefiniteAnimatedView.center = CGPointMake(CGRectGetMidX(self.hudView.bounds), centerY);
|
||||
if(self.progress != SVProgressHUDUndefinedProgress) {
|
||||
self.backgroundRingView.center = self.ringView.center = CGPointMake(CGRectGetMidX(self.hudView.bounds), centerY);
|
||||
}
|
||||
self.imageView.center = CGPointMake(CGRectGetMidX(self.hudView.bounds), centerY);
|
||||
|
||||
// Label
|
||||
if(imageUsed || progressUsed) {
|
||||
centerY = CGRectGetMaxY(imageUsed ? self.imageView.frame : self.indefiniteAnimatedView.frame) + SVProgressHUDLabelSpacing + labelHeight / 2.0f;
|
||||
} else {
|
||||
centerY = CGRectGetMidY(self.hudView.bounds);
|
||||
}
|
||||
self.statusLabel.frame = labelRect;
|
||||
self.statusLabel.center = CGPointMake(CGRectGetMidX(self.hudView.bounds), centerY);
|
||||
|
||||
[CATransaction commit];
|
||||
}
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
- (void)updateMotionEffectForOrientation:(UIInterfaceOrientation)orientation {
|
||||
UIInterpolatingMotionEffectType xMotionEffectType = UIInterfaceOrientationIsPortrait(orientation) ? UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis : UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis;
|
||||
UIInterpolatingMotionEffectType yMotionEffectType = UIInterfaceOrientationIsPortrait(orientation) ? UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis : UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis;
|
||||
[self updateMotionEffectForXMotionEffectType:xMotionEffectType yMotionEffectType:yMotionEffectType];
|
||||
}
|
||||
#endif
|
||||
|
||||
- (void)updateMotionEffectForXMotionEffectType:(UIInterpolatingMotionEffectType)xMotionEffectType yMotionEffectType:(UIInterpolatingMotionEffectType)yMotionEffectType {
|
||||
UIInterpolatingMotionEffect *effectX = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.x" type:xMotionEffectType];
|
||||
effectX.minimumRelativeValue = @(-SVProgressHUDParallaxDepthPoints);
|
||||
effectX.maximumRelativeValue = @(SVProgressHUDParallaxDepthPoints);
|
||||
|
||||
UIInterpolatingMotionEffect *effectY = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.y" type:yMotionEffectType];
|
||||
effectY.minimumRelativeValue = @(-SVProgressHUDParallaxDepthPoints);
|
||||
effectY.maximumRelativeValue = @(SVProgressHUDParallaxDepthPoints);
|
||||
|
||||
UIMotionEffectGroup *effectGroup = [UIMotionEffectGroup new];
|
||||
effectGroup.motionEffects = @[effectX, effectY];
|
||||
|
||||
// Clear old motion effect, then add new motion effects
|
||||
self.hudView.motionEffects = @[];
|
||||
[self.hudView addMotionEffect:effectGroup];
|
||||
}
|
||||
|
||||
- (void)updateViewHierarchy {
|
||||
// Add the overlay to the application window if necessary
|
||||
if(!self.controlView.superview) {
|
||||
if(self.containerView){
|
||||
[self.containerView addSubview:self.controlView];
|
||||
} else {
|
||||
#if !defined(SV_APP_EXTENSIONS)
|
||||
[self.frontWindow addSubview:self.controlView];
|
||||
#else
|
||||
// If SVProgressHUD is used inside an app extension add it to the given view
|
||||
if(self.viewForExtension) {
|
||||
[self.viewForExtension addSubview:self.controlView];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
// The HUD is already on screen, but maybe not in front. Therefore
|
||||
// ensure that overlay will be on top of rootViewController (which may
|
||||
// be changed during runtime).
|
||||
[self.controlView.superview bringSubviewToFront:self.controlView];
|
||||
}
|
||||
|
||||
// Add self to the overlay view
|
||||
if(!self.superview) {
|
||||
[self.controlView addSubview:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setStatus:(NSString*)status {
|
||||
self.statusLabel.text = status;
|
||||
self.statusLabel.hidden = status.length == 0;
|
||||
[self updateHUDFrame];
|
||||
}
|
||||
|
||||
- (void)setGraceTimer:(NSTimer*)timer {
|
||||
if(_graceTimer) {
|
||||
[_graceTimer invalidate];
|
||||
_graceTimer = nil;
|
||||
}
|
||||
if(timer) {
|
||||
_graceTimer = timer;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setFadeOutTimer:(NSTimer*)timer {
|
||||
if(_fadeOutTimer) {
|
||||
[_fadeOutTimer invalidate];
|
||||
_fadeOutTimer = nil;
|
||||
}
|
||||
if(timer) {
|
||||
_fadeOutTimer = timer;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Notifications and their handling
|
||||
|
||||
- (void)registerNotifications {
|
||||
#if TARGET_OS_IOS
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(positionHUD:)
|
||||
name:UIApplicationDidChangeStatusBarOrientationNotification
|
||||
object:nil];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(positionHUD:)
|
||||
name:UIKeyboardWillHideNotification
|
||||
object:nil];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(positionHUD:)
|
||||
name:UIKeyboardDidHideNotification
|
||||
object:nil];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(positionHUD:)
|
||||
name:UIKeyboardWillShowNotification
|
||||
object:nil];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(positionHUD:)
|
||||
name:UIKeyboardDidShowNotification
|
||||
object:nil];
|
||||
#endif
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(positionHUD:)
|
||||
name:UIApplicationDidBecomeActiveNotification
|
||||
object:nil];
|
||||
}
|
||||
|
||||
- (NSDictionary*)notificationUserInfo {
|
||||
return (self.statusLabel.text ? @{SVProgressHUDStatusUserInfoKey : self.statusLabel.text} : nil);
|
||||
}
|
||||
|
||||
- (void)positionHUD:(NSNotification*)notification {
|
||||
CGFloat keyboardHeight = 0.0f;
|
||||
double animationDuration = 0.0;
|
||||
|
||||
#if !defined(SV_APP_EXTENSIONS) && TARGET_OS_IOS
|
||||
self.frame = [[[UIApplication sharedApplication] delegate] window].bounds;
|
||||
UIInterfaceOrientation orientation = UIApplication.sharedApplication.statusBarOrientation;
|
||||
#elif !defined(SV_APP_EXTENSIONS) && !TARGET_OS_IOS
|
||||
self.frame= [UIApplication sharedApplication].keyWindow.bounds;
|
||||
#else
|
||||
if (self.viewForExtension) {
|
||||
self.frame = self.viewForExtension.frame;
|
||||
} else {
|
||||
self.frame = UIScreen.mainScreen.bounds;
|
||||
}
|
||||
#if TARGET_OS_IOS
|
||||
UIInterfaceOrientation orientation = CGRectGetWidth(self.frame) > CGRectGetHeight(self.frame) ? UIInterfaceOrientationLandscapeLeft : UIInterfaceOrientationPortrait;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
// Get keyboardHeight in regard to current state
|
||||
if(notification) {
|
||||
NSDictionary* keyboardInfo = [notification userInfo];
|
||||
CGRect keyboardFrame = [keyboardInfo[UIKeyboardFrameBeginUserInfoKey] CGRectValue];
|
||||
animationDuration = [keyboardInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
|
||||
|
||||
if(notification.name == UIKeyboardWillShowNotification || notification.name == UIKeyboardDidShowNotification) {
|
||||
keyboardHeight = CGRectGetWidth(keyboardFrame);
|
||||
|
||||
if(UIInterfaceOrientationIsPortrait(orientation)) {
|
||||
keyboardHeight = CGRectGetHeight(keyboardFrame);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
keyboardHeight = self.visibleKeyboardHeight;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Get the currently active frame of the display (depends on orientation)
|
||||
CGRect orientationFrame = self.bounds;
|
||||
|
||||
#if !defined(SV_APP_EXTENSIONS) && TARGET_OS_IOS
|
||||
CGRect statusBarFrame = UIApplication.sharedApplication.statusBarFrame;
|
||||
#else
|
||||
CGRect statusBarFrame = CGRectZero;
|
||||
#endif
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
// Update the motion effects in regard to orientation
|
||||
[self updateMotionEffectForOrientation:orientation];
|
||||
#else
|
||||
[self updateMotionEffectForXMotionEffectType:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis yMotionEffectType:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis];
|
||||
#endif
|
||||
|
||||
// Calculate available height for display
|
||||
CGFloat activeHeight = CGRectGetHeight(orientationFrame);
|
||||
if(keyboardHeight > 0) {
|
||||
activeHeight += CGRectGetHeight(statusBarFrame) * 2;
|
||||
}
|
||||
activeHeight -= keyboardHeight;
|
||||
|
||||
CGFloat posX = CGRectGetMidX(orientationFrame);
|
||||
CGFloat posY = floorf(activeHeight*0.45f);
|
||||
|
||||
CGFloat rotateAngle = 0.0;
|
||||
CGPoint newCenter = CGPointMake(posX, posY);
|
||||
|
||||
if(notification) {
|
||||
// Animate update if notification was present
|
||||
[UIView animateWithDuration:animationDuration
|
||||
delay:0
|
||||
options:(UIViewAnimationOptions) (UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionBeginFromCurrentState)
|
||||
animations:^{
|
||||
[self moveToPoint:newCenter rotateAngle:rotateAngle];
|
||||
[self.hudView setNeedsDisplay];
|
||||
} completion:nil];
|
||||
} else {
|
||||
[self moveToPoint:newCenter rotateAngle:rotateAngle];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)moveToPoint:(CGPoint)newCenter rotateAngle:(CGFloat)angle {
|
||||
self.hudView.transform = CGAffineTransformMakeRotation(angle);
|
||||
if (self.containerView) {
|
||||
self.hudView.center = CGPointMake(self.containerView.center.x + self.offsetFromCenter.horizontal, self.containerView.center.y + self.offsetFromCenter.vertical);
|
||||
} else {
|
||||
self.hudView.center = CGPointMake(newCenter.x + self.offsetFromCenter.horizontal, newCenter.y + self.offsetFromCenter.vertical);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Event handling
|
||||
|
||||
- (void)controlViewDidReceiveTouchEvent:(id)sender forEvent:(UIEvent*)event {
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:SVProgressHUDDidReceiveTouchEventNotification
|
||||
object:self
|
||||
userInfo:[self notificationUserInfo]];
|
||||
|
||||
UITouch *touch = event.allTouches.anyObject;
|
||||
CGPoint touchLocation = [touch locationInView:self];
|
||||
|
||||
if(CGRectContainsPoint(self.hudView.frame, touchLocation)) {
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:SVProgressHUDDidTouchDownInsideNotification
|
||||
object:self
|
||||
userInfo:[self notificationUserInfo]];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Master show/dismiss methods
|
||||
|
||||
- (void)showProgress:(float)progress status:(NSString*)status {
|
||||
__weak SVProgressHUD *weakSelf = self;
|
||||
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
|
||||
__strong SVProgressHUD *strongSelf = weakSelf;
|
||||
if(strongSelf){
|
||||
if(strongSelf.fadeOutTimer) {
|
||||
strongSelf.activityCount = 0;
|
||||
}
|
||||
|
||||
// Stop timer
|
||||
strongSelf.fadeOutTimer = nil;
|
||||
strongSelf.graceTimer = nil;
|
||||
|
||||
// Update / Check view hierarchy to ensure the HUD is visible
|
||||
[strongSelf updateViewHierarchy];
|
||||
|
||||
// Reset imageView and fadeout timer if an image is currently displayed
|
||||
strongSelf.imageView.hidden = YES;
|
||||
strongSelf.imageView.image = nil;
|
||||
|
||||
// Update text and set progress to the given value
|
||||
strongSelf.statusLabel.hidden = status.length == 0;
|
||||
strongSelf.statusLabel.text = status;
|
||||
strongSelf.progress = progress;
|
||||
|
||||
// Choose the "right" indicator depending on the progress
|
||||
if(progress >= 0) {
|
||||
// Cancel the indefiniteAnimatedView, then show the ringLayer
|
||||
[strongSelf cancelIndefiniteAnimatedViewAnimation];
|
||||
|
||||
// Add ring to HUD
|
||||
if(!strongSelf.ringView.superview){
|
||||
[strongSelf.hudView.contentView addSubview:strongSelf.ringView];
|
||||
}
|
||||
if(!strongSelf.backgroundRingView.superview){
|
||||
[strongSelf.hudView.contentView addSubview:strongSelf.backgroundRingView];
|
||||
}
|
||||
|
||||
// Set progress animated
|
||||
[CATransaction begin];
|
||||
[CATransaction setDisableActions:YES];
|
||||
strongSelf.ringView.strokeEnd = progress;
|
||||
[CATransaction commit];
|
||||
|
||||
// Update the activity count
|
||||
if(progress == 0) {
|
||||
strongSelf.activityCount++;
|
||||
}
|
||||
} else {
|
||||
// Cancel the ringLayer animation, then show the indefiniteAnimatedView
|
||||
[strongSelf cancelRingLayerAnimation];
|
||||
|
||||
// Add indefiniteAnimatedView to HUD
|
||||
[strongSelf.hudView.contentView addSubview:strongSelf.indefiniteAnimatedView];
|
||||
if([strongSelf.indefiniteAnimatedView respondsToSelector:@selector(startAnimating)]) {
|
||||
[(id)strongSelf.indefiniteAnimatedView startAnimating];
|
||||
}
|
||||
|
||||
// Update the activity count
|
||||
strongSelf.activityCount++;
|
||||
}
|
||||
|
||||
// Fade in delayed if a grace time is set
|
||||
if (self.graceTimeInterval > 0.0 && self.backgroundView.alpha == 0.0f) {
|
||||
strongSelf.graceTimer = [NSTimer timerWithTimeInterval:self.graceTimeInterval target:strongSelf selector:@selector(fadeIn:) userInfo:nil repeats:NO];
|
||||
[[NSRunLoop mainRunLoop] addTimer:strongSelf.graceTimer forMode:NSRunLoopCommonModes];
|
||||
} else {
|
||||
[strongSelf fadeIn:nil];
|
||||
}
|
||||
|
||||
// Tell the Haptics Generator to prepare for feedback, which may come soon
|
||||
#if TARGET_OS_IOS && __IPHONE_OS_VERSION_MAX_ALLOWED >= 100000
|
||||
if (@available(iOS 10.0, *)) {
|
||||
[strongSelf.hapticGenerator prepare];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)showImage:(UIImage*)image status:(NSString*)status duration:(NSTimeInterval)duration {
|
||||
__weak SVProgressHUD *weakSelf = self;
|
||||
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
|
||||
__strong SVProgressHUD *strongSelf = weakSelf;
|
||||
if(strongSelf){
|
||||
// Stop timer
|
||||
strongSelf.fadeOutTimer = nil;
|
||||
strongSelf.graceTimer = nil;
|
||||
|
||||
// Update / Check view hierarchy to ensure the HUD is visible
|
||||
[strongSelf updateViewHierarchy];
|
||||
|
||||
// Reset progress and cancel any running animation
|
||||
strongSelf.progress = SVProgressHUDUndefinedProgress;
|
||||
[strongSelf cancelRingLayerAnimation];
|
||||
[strongSelf cancelIndefiniteAnimatedViewAnimation];
|
||||
|
||||
// Update imageView
|
||||
if (self.shouldTintImages) {
|
||||
if (image.renderingMode != UIImageRenderingModeAlwaysTemplate) {
|
||||
strongSelf.imageView.image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
|
||||
}
|
||||
strongSelf.imageView.tintColor = strongSelf.foregroundColorForStyle;;
|
||||
} else {
|
||||
strongSelf.imageView.image = image;
|
||||
}
|
||||
strongSelf.imageView.hidden = NO;
|
||||
|
||||
// Update text
|
||||
strongSelf.statusLabel.hidden = status.length == 0;
|
||||
strongSelf.statusLabel.text = status;
|
||||
|
||||
// Fade in delayed if a grace time is set
|
||||
// An image will be dismissed automatically. Thus pass the duration as userInfo.
|
||||
if (self.graceTimeInterval > 0.0 && self.backgroundView.alpha == 0.0f) {
|
||||
strongSelf.graceTimer = [NSTimer timerWithTimeInterval:self.graceTimeInterval target:strongSelf selector:@selector(fadeIn:) userInfo:@(duration) repeats:NO];
|
||||
[[NSRunLoop mainRunLoop] addTimer:strongSelf.graceTimer forMode:NSRunLoopCommonModes];
|
||||
} else {
|
||||
[strongSelf fadeIn:@(duration)];
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)fadeIn:(id)data {
|
||||
// Update the HUDs frame to the new content and position HUD
|
||||
[self updateHUDFrame];
|
||||
[self positionHUD:nil];
|
||||
|
||||
// Update accessibility as well as user interaction
|
||||
if(self.defaultMaskType != SVProgressHUDMaskTypeNone) {
|
||||
self.controlView.userInteractionEnabled = YES;
|
||||
self.accessibilityLabel = self.statusLabel.text ?: NSLocalizedString(@"Loading", nil);
|
||||
self.isAccessibilityElement = YES;
|
||||
} else {
|
||||
self.controlView.userInteractionEnabled = NO;
|
||||
self.hudView.accessibilityLabel = self.statusLabel.text ?: NSLocalizedString(@"Loading", nil);
|
||||
self.hudView.isAccessibilityElement = YES;
|
||||
}
|
||||
|
||||
// Get duration
|
||||
id duration = [data isKindOfClass:[NSTimer class]] ? ((NSTimer *)data).userInfo : data;
|
||||
|
||||
// Show if not already visible
|
||||
if(self.backgroundView.alpha != 1.0f) {
|
||||
// Post notification to inform user
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:SVProgressHUDWillAppearNotification
|
||||
object:self
|
||||
userInfo:[self notificationUserInfo]];
|
||||
|
||||
// Shrink HUD to to make a nice appear / pop up animation
|
||||
self.hudView.transform = self.hudView.transform = CGAffineTransformScale(self.hudView.transform, 1/1.5f, 1/1.5f);
|
||||
|
||||
__block void (^animationsBlock)(void) = ^{
|
||||
// Zoom HUD a little to make a nice appear / pop up animation
|
||||
self.hudView.transform = CGAffineTransformIdentity;
|
||||
|
||||
// Fade in all effects (colors, blur, etc.)
|
||||
[self fadeInEffects];
|
||||
};
|
||||
|
||||
__block void (^completionBlock)(void) = ^{
|
||||
// Check if we really achieved to show the HUD (<=> alpha)
|
||||
// and the change of these values has not been cancelled in between e.g. due to a dismissal
|
||||
if(self.backgroundView.alpha == 1.0f){
|
||||
// Register observer <=> we now have to handle orientation changes etc.
|
||||
[self registerNotifications];
|
||||
|
||||
// Post notification to inform user
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:SVProgressHUDDidAppearNotification
|
||||
object:self
|
||||
userInfo:[self notificationUserInfo]];
|
||||
|
||||
// Update accessibility
|
||||
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, nil);
|
||||
UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, self.statusLabel.text);
|
||||
|
||||
// Dismiss automatically if a duration was passed as userInfo. We start a timer
|
||||
// which then will call dismiss after the predefined duration
|
||||
if(duration){
|
||||
self.fadeOutTimer = [NSTimer timerWithTimeInterval:[(NSNumber *)duration doubleValue] target:self selector:@selector(dismiss) userInfo:nil repeats:NO];
|
||||
[[NSRunLoop mainRunLoop] addTimer:self.fadeOutTimer forMode:NSRunLoopCommonModes];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Animate appearance
|
||||
if (self.fadeInAnimationDuration > 0) {
|
||||
// Animate appearance
|
||||
[UIView animateWithDuration:self.fadeInAnimationDuration
|
||||
delay:0
|
||||
options:(UIViewAnimationOptions) (UIViewAnimationOptionAllowUserInteraction | UIViewAnimationCurveEaseIn | UIViewAnimationOptionBeginFromCurrentState)
|
||||
animations:^{
|
||||
animationsBlock();
|
||||
} completion:^(BOOL finished) {
|
||||
completionBlock();
|
||||
}];
|
||||
} else {
|
||||
animationsBlock();
|
||||
completionBlock();
|
||||
}
|
||||
|
||||
// Inform iOS to redraw the view hierarchy
|
||||
[self setNeedsDisplay];
|
||||
} else {
|
||||
// Update accessibility
|
||||
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, nil);
|
||||
UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, self.statusLabel.text);
|
||||
|
||||
// Dismiss automatically if a duration was passed as userInfo. We start a timer
|
||||
// which then will call dismiss after the predefined duration
|
||||
if(duration){
|
||||
self.fadeOutTimer = [NSTimer timerWithTimeInterval:[(NSNumber *)duration doubleValue] target:self selector:@selector(dismiss) userInfo:nil repeats:NO];
|
||||
[[NSRunLoop mainRunLoop] addTimer:self.fadeOutTimer forMode:NSRunLoopCommonModes];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)dismiss {
|
||||
[self dismissWithDelay:0.0 completion:nil];
|
||||
}
|
||||
|
||||
- (void)dismissWithDelay:(NSTimeInterval)delay completion:(SVProgressHUDDismissCompletion)completion {
|
||||
__weak SVProgressHUD *weakSelf = self;
|
||||
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
|
||||
__strong SVProgressHUD *strongSelf = weakSelf;
|
||||
if(strongSelf){
|
||||
// Stop timer
|
||||
strongSelf.graceTimer = nil;
|
||||
|
||||
// Post notification to inform user
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:SVProgressHUDWillDisappearNotification
|
||||
object:nil
|
||||
userInfo:[strongSelf notificationUserInfo]];
|
||||
|
||||
// Reset activity count
|
||||
strongSelf.activityCount = 0;
|
||||
|
||||
__block void (^animationsBlock)(void) = ^{
|
||||
// Shrink HUD a little to make a nice disappear animation
|
||||
strongSelf.hudView.transform = CGAffineTransformScale(strongSelf.hudView.transform, 1/1.3f, 1/1.3f);
|
||||
|
||||
// Fade out all effects (colors, blur, etc.)
|
||||
[strongSelf fadeOutEffects];
|
||||
};
|
||||
|
||||
__block void (^completionBlock)(void) = ^{
|
||||
// Check if we really achieved to dismiss the HUD (<=> alpha values are applied)
|
||||
// and the change of these values has not been cancelled in between e.g. due to a new show
|
||||
if(self.backgroundView.alpha == 0.0f){
|
||||
// Clean up view hierarchy (overlays)
|
||||
[strongSelf.controlView removeFromSuperview];
|
||||
[strongSelf.backgroundView removeFromSuperview];
|
||||
[strongSelf.hudView removeFromSuperview];
|
||||
[strongSelf removeFromSuperview];
|
||||
|
||||
// Reset progress and cancel any running animation
|
||||
strongSelf.progress = SVProgressHUDUndefinedProgress;
|
||||
[strongSelf cancelRingLayerAnimation];
|
||||
[strongSelf cancelIndefiniteAnimatedViewAnimation];
|
||||
|
||||
// Remove observer <=> we do not have to handle orientation changes etc.
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:strongSelf];
|
||||
|
||||
// Post notification to inform user
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:SVProgressHUDDidDisappearNotification
|
||||
object:strongSelf
|
||||
userInfo:[strongSelf notificationUserInfo]];
|
||||
|
||||
// Tell the rootViewController to update the StatusBar appearance
|
||||
#if !defined(SV_APP_EXTENSIONS) && TARGET_OS_IOS
|
||||
UIViewController *rootController = [[UIApplication sharedApplication] keyWindow].rootViewController;
|
||||
[rootController setNeedsStatusBarAppearanceUpdate];
|
||||
#endif
|
||||
|
||||
// Run an (optional) completionHandler
|
||||
if (completion) {
|
||||
completion();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// UIViewAnimationOptionBeginFromCurrentState AND a delay doesn't always work as expected
|
||||
// When UIViewAnimationOptionBeginFromCurrentState is set, animateWithDuration: evaluates the current
|
||||
// values to check if an animation is necessary. The evaluation happens at function call time and not
|
||||
// after the delay => the animation is sometimes skipped. Therefore we delay using dispatch_after.
|
||||
|
||||
dispatch_time_t dipatchTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC));
|
||||
dispatch_after(dipatchTime, dispatch_get_main_queue(), ^{
|
||||
if (strongSelf.fadeOutAnimationDuration > 0) {
|
||||
// Animate appearance
|
||||
[UIView animateWithDuration:strongSelf.fadeOutAnimationDuration
|
||||
delay:0
|
||||
options:(UIViewAnimationOptions) (UIViewAnimationOptionAllowUserInteraction | UIViewAnimationCurveEaseOut | UIViewAnimationOptionBeginFromCurrentState)
|
||||
animations:^{
|
||||
animationsBlock();
|
||||
} completion:^(BOOL finished) {
|
||||
completionBlock();
|
||||
}];
|
||||
} else {
|
||||
animationsBlock();
|
||||
completionBlock();
|
||||
}
|
||||
});
|
||||
|
||||
// Inform iOS to redraw the view hierarchy
|
||||
[strongSelf setNeedsDisplay];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Ring progress animation
|
||||
|
||||
- (UIView*)indefiniteAnimatedView {
|
||||
// Get the correct spinner for defaultAnimationType
|
||||
if(self.defaultAnimationType == SVProgressHUDAnimationTypeFlat){
|
||||
// Check if spinner exists and is an object of different class
|
||||
if(_indefiniteAnimatedView && ![_indefiniteAnimatedView isKindOfClass:[SVIndefiniteAnimatedView class]]){
|
||||
[_indefiniteAnimatedView removeFromSuperview];
|
||||
_indefiniteAnimatedView = nil;
|
||||
}
|
||||
|
||||
if(!_indefiniteAnimatedView){
|
||||
_indefiniteAnimatedView = [[SVIndefiniteAnimatedView alloc] initWithFrame:CGRectZero];
|
||||
}
|
||||
|
||||
// Update styling
|
||||
SVIndefiniteAnimatedView *indefiniteAnimatedView = (SVIndefiniteAnimatedView*)_indefiniteAnimatedView;
|
||||
indefiniteAnimatedView.strokeColor = self.foregroundColorForStyle;
|
||||
indefiniteAnimatedView.strokeThickness = self.ringThickness;
|
||||
indefiniteAnimatedView.radius = self.statusLabel.text ? self.ringRadius : self.ringNoTextRadius;
|
||||
} else {
|
||||
// Check if spinner exists and is an object of different class
|
||||
if(_indefiniteAnimatedView && ![_indefiniteAnimatedView isKindOfClass:[UIActivityIndicatorView class]]){
|
||||
[_indefiniteAnimatedView removeFromSuperview];
|
||||
_indefiniteAnimatedView = nil;
|
||||
}
|
||||
|
||||
if(!_indefiniteAnimatedView){
|
||||
_indefiniteAnimatedView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
|
||||
}
|
||||
|
||||
// Update styling
|
||||
UIActivityIndicatorView *activityIndicatorView = (UIActivityIndicatorView*)_indefiniteAnimatedView;
|
||||
activityIndicatorView.color = self.foregroundColorForStyle;
|
||||
}
|
||||
[_indefiniteAnimatedView sizeToFit];
|
||||
|
||||
return _indefiniteAnimatedView;
|
||||
}
|
||||
|
||||
- (SVProgressAnimatedView*)ringView {
|
||||
if(!_ringView) {
|
||||
_ringView = [[SVProgressAnimatedView alloc] initWithFrame:CGRectZero];
|
||||
}
|
||||
|
||||
// Update styling
|
||||
_ringView.strokeColor = self.foregroundColorForStyle;
|
||||
_ringView.strokeThickness = self.ringThickness;
|
||||
_ringView.radius = self.statusLabel.text ? self.ringRadius : self.ringNoTextRadius;
|
||||
|
||||
return _ringView;
|
||||
}
|
||||
|
||||
- (SVProgressAnimatedView*)backgroundRingView {
|
||||
if(!_backgroundRingView) {
|
||||
_backgroundRingView = [[SVProgressAnimatedView alloc] initWithFrame:CGRectZero];
|
||||
_backgroundRingView.strokeEnd = 1.0f;
|
||||
}
|
||||
|
||||
// Update styling
|
||||
_backgroundRingView.strokeColor = [self.foregroundColorForStyle colorWithAlphaComponent:0.1f];
|
||||
_backgroundRingView.strokeThickness = self.ringThickness;
|
||||
_backgroundRingView.radius = self.statusLabel.text ? self.ringRadius : self.ringNoTextRadius;
|
||||
|
||||
return _backgroundRingView;
|
||||
}
|
||||
|
||||
- (void)cancelRingLayerAnimation {
|
||||
// Animate value update, stop animation
|
||||
[CATransaction begin];
|
||||
[CATransaction setDisableActions:YES];
|
||||
|
||||
[self.hudView.layer removeAllAnimations];
|
||||
self.ringView.strokeEnd = 0.0f;
|
||||
|
||||
[CATransaction commit];
|
||||
|
||||
// Remove from view
|
||||
[self.ringView removeFromSuperview];
|
||||
[self.backgroundRingView removeFromSuperview];
|
||||
}
|
||||
|
||||
- (void)cancelIndefiniteAnimatedViewAnimation {
|
||||
// Stop animation
|
||||
if([self.indefiniteAnimatedView respondsToSelector:@selector(stopAnimating)]) {
|
||||
[(id)self.indefiniteAnimatedView stopAnimating];
|
||||
}
|
||||
// Remove from view
|
||||
[self.indefiniteAnimatedView removeFromSuperview];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Utilities
|
||||
|
||||
+ (BOOL)isVisible {
|
||||
// Checking one alpha value is sufficient as they are all the same
|
||||
return [self sharedView].backgroundView.alpha > 0.0f;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Getters
|
||||
|
||||
+ (NSTimeInterval)displayDurationForString:(NSString*)string {
|
||||
CGFloat minimum = MAX((CGFloat)string.length * 0.06 + 0.5, [self sharedView].minimumDismissTimeInterval);
|
||||
return MIN(minimum, [self sharedView].maximumDismissTimeInterval);
|
||||
}
|
||||
|
||||
- (UIColor*)foregroundColorForStyle {
|
||||
if(self.defaultStyle == SVProgressHUDStyleLight) {
|
||||
return [UIColor blackColor];
|
||||
} else if(self.defaultStyle == SVProgressHUDStyleDark) {
|
||||
return [UIColor whiteColor];
|
||||
} else {
|
||||
return self.foregroundColor;
|
||||
}
|
||||
}
|
||||
|
||||
- (UIColor*)backgroundColorForStyle {
|
||||
if(self.defaultStyle == SVProgressHUDStyleLight) {
|
||||
return [UIColor whiteColor];
|
||||
} else if(self.defaultStyle == SVProgressHUDStyleDark) {
|
||||
return [UIColor blackColor];
|
||||
} else {
|
||||
return self.backgroundColor;
|
||||
}
|
||||
}
|
||||
|
||||
- (UIControl*)controlView {
|
||||
if(!_controlView) {
|
||||
_controlView = [UIControl new];
|
||||
_controlView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
_controlView.backgroundColor = [UIColor clearColor];
|
||||
_controlView.userInteractionEnabled = YES;
|
||||
[_controlView addTarget:self action:@selector(controlViewDidReceiveTouchEvent:forEvent:) forControlEvents:UIControlEventTouchDown];
|
||||
}
|
||||
|
||||
// Update frames
|
||||
#if !defined(SV_APP_EXTENSIONS)
|
||||
CGRect windowBounds = [[[UIApplication sharedApplication] delegate] window].bounds;
|
||||
_controlView.frame = windowBounds;
|
||||
#else
|
||||
_controlView.frame = [UIScreen mainScreen].bounds;
|
||||
#endif
|
||||
|
||||
return _controlView;
|
||||
}
|
||||
|
||||
-(UIView *)backgroundView {
|
||||
if(!_backgroundView){
|
||||
_backgroundView = [UIView new];
|
||||
_backgroundView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
}
|
||||
if(!_backgroundView.superview){
|
||||
[self insertSubview:_backgroundView belowSubview:self.hudView];
|
||||
}
|
||||
|
||||
// Update styling
|
||||
if(self.defaultMaskType == SVProgressHUDMaskTypeGradient){
|
||||
if(!_backgroundRadialGradientLayer){
|
||||
_backgroundRadialGradientLayer = [SVRadialGradientLayer layer];
|
||||
}
|
||||
if(!_backgroundRadialGradientLayer.superlayer){
|
||||
[_backgroundView.layer insertSublayer:_backgroundRadialGradientLayer atIndex:0];
|
||||
}
|
||||
_backgroundView.backgroundColor = [UIColor clearColor];
|
||||
} else {
|
||||
if(_backgroundRadialGradientLayer && _backgroundRadialGradientLayer.superlayer){
|
||||
[_backgroundRadialGradientLayer removeFromSuperlayer];
|
||||
}
|
||||
if(self.defaultMaskType == SVProgressHUDMaskTypeBlack){
|
||||
_backgroundView.backgroundColor = [UIColor colorWithWhite:0 alpha:0.4];
|
||||
} else if(self.defaultMaskType == SVProgressHUDMaskTypeCustom){
|
||||
_backgroundView.backgroundColor = self.backgroundLayerColor;
|
||||
} else {
|
||||
_backgroundView.backgroundColor = [UIColor clearColor];
|
||||
}
|
||||
}
|
||||
|
||||
// Update frame
|
||||
if(_backgroundView){
|
||||
_backgroundView.frame = self.bounds;
|
||||
}
|
||||
if(_backgroundRadialGradientLayer){
|
||||
_backgroundRadialGradientLayer.frame = self.bounds;
|
||||
|
||||
// Calculate the new center of the gradient, it may change if keyboard is visible
|
||||
CGPoint gradientCenter = self.center;
|
||||
gradientCenter.y = (self.bounds.size.height - self.visibleKeyboardHeight)/2;
|
||||
_backgroundRadialGradientLayer.gradientCenter = gradientCenter;
|
||||
[_backgroundRadialGradientLayer setNeedsDisplay];
|
||||
}
|
||||
|
||||
return _backgroundView;
|
||||
}
|
||||
- (UIVisualEffectView*)hudView {
|
||||
if(!_hudView) {
|
||||
_hudView = [UIVisualEffectView new];
|
||||
_hudView.layer.masksToBounds = YES;
|
||||
_hudView.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin;
|
||||
}
|
||||
if(!_hudView.superview) {
|
||||
[self addSubview:_hudView];
|
||||
}
|
||||
|
||||
// Update styling
|
||||
_hudView.layer.cornerRadius = self.cornerRadius;
|
||||
|
||||
return _hudView;
|
||||
}
|
||||
|
||||
- (UILabel*)statusLabel {
|
||||
if(!_statusLabel) {
|
||||
_statusLabel = [[UILabel alloc] initWithFrame:CGRectZero];
|
||||
_statusLabel.backgroundColor = [UIColor clearColor];
|
||||
_statusLabel.adjustsFontSizeToFitWidth = YES;
|
||||
_statusLabel.textAlignment = NSTextAlignmentCenter;
|
||||
_statusLabel.baselineAdjustment = UIBaselineAdjustmentAlignCenters;
|
||||
_statusLabel.numberOfLines = 0;
|
||||
}
|
||||
if(!_statusLabel.superview) {
|
||||
[self.hudView.contentView addSubview:_statusLabel];
|
||||
}
|
||||
|
||||
// Update styling
|
||||
_statusLabel.textColor = self.foregroundColorForStyle;
|
||||
_statusLabel.font = self.font;
|
||||
|
||||
return _statusLabel;
|
||||
}
|
||||
|
||||
- (UIImageView*)imageView {
|
||||
if(_imageView && !CGSizeEqualToSize(_imageView.bounds.size, _imageViewSize)) {
|
||||
[_imageView removeFromSuperview];
|
||||
_imageView = nil;
|
||||
}
|
||||
|
||||
if(!_imageView) {
|
||||
_imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, _imageViewSize.width, _imageViewSize.height)];
|
||||
}
|
||||
if(!_imageView.superview) {
|
||||
[self.hudView.contentView addSubview:_imageView];
|
||||
}
|
||||
|
||||
return _imageView;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Helper
|
||||
|
||||
- (CGFloat)visibleKeyboardHeight {
|
||||
#if !defined(SV_APP_EXTENSIONS)
|
||||
UIWindow *keyboardWindow = nil;
|
||||
for (UIWindow *testWindow in UIApplication.sharedApplication.windows) {
|
||||
if(![testWindow.class isEqual:UIWindow.class]) {
|
||||
keyboardWindow = testWindow;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (__strong UIView *possibleKeyboard in keyboardWindow.subviews) {
|
||||
NSString *viewName = NSStringFromClass(possibleKeyboard.class);
|
||||
if([viewName hasPrefix:@"UI"]){
|
||||
if([viewName hasSuffix:@"PeripheralHostView"] || [viewName hasSuffix:@"Keyboard"]){
|
||||
return CGRectGetHeight(possibleKeyboard.bounds);
|
||||
} else if ([viewName hasSuffix:@"InputSetContainerView"]){
|
||||
for (__strong UIView *possibleKeyboardSubview in possibleKeyboard.subviews) {
|
||||
viewName = NSStringFromClass(possibleKeyboardSubview.class);
|
||||
if([viewName hasPrefix:@"UI"] && [viewName hasSuffix:@"InputSetHostView"]) {
|
||||
CGRect convertedRect = [possibleKeyboard convertRect:possibleKeyboardSubview.frame toView:self];
|
||||
CGRect intersectedRect = CGRectIntersection(convertedRect, self.bounds);
|
||||
if (!CGRectIsNull(intersectedRect)) {
|
||||
return CGRectGetHeight(intersectedRect);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (UIWindow *)frontWindow {
|
||||
#if !defined(SV_APP_EXTENSIONS)
|
||||
NSEnumerator *frontToBackWindows = [UIApplication.sharedApplication.windows reverseObjectEnumerator];
|
||||
for (UIWindow *window in frontToBackWindows) {
|
||||
BOOL windowOnMainScreen = window.screen == UIScreen.mainScreen;
|
||||
BOOL windowIsVisible = !window.hidden && window.alpha > 0;
|
||||
BOOL windowLevelSupported = (window.windowLevel >= UIWindowLevelNormal && window.windowLevel <= self.maxSupportedWindowLevel);
|
||||
BOOL windowKeyWindow = window.isKeyWindow;
|
||||
|
||||
if(windowOnMainScreen && windowIsVisible && windowLevelSupported && windowKeyWindow) {
|
||||
return window;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)fadeInEffects {
|
||||
if(self.defaultStyle != SVProgressHUDStyleCustom) {
|
||||
// Add blur effect
|
||||
UIBlurEffectStyle blurEffectStyle = self.defaultStyle == SVProgressHUDStyleDark ? UIBlurEffectStyleDark : UIBlurEffectStyleLight;
|
||||
UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:blurEffectStyle];
|
||||
self.hudView.effect = blurEffect;
|
||||
|
||||
// We omit UIVibrancy effect and use a suitable background color as an alternative.
|
||||
// This will make everything more readable. See the following for details:
|
||||
// https://www.omnigroup.com/developer/how-to-make-text-in-a-uivisualeffectview-readable-on-any-background
|
||||
|
||||
self.hudView.backgroundColor = [self.backgroundColorForStyle colorWithAlphaComponent:0.6f];
|
||||
} else {
|
||||
self.hudView.backgroundColor = self.backgroundColorForStyle;
|
||||
}
|
||||
|
||||
// Fade in views
|
||||
self.backgroundView.alpha = 1.0f;
|
||||
|
||||
self.imageView.alpha = 1.0f;
|
||||
self.statusLabel.alpha = 1.0f;
|
||||
self.indefiniteAnimatedView.alpha = 1.0f;
|
||||
self.ringView.alpha = self.backgroundRingView.alpha = 1.0f;
|
||||
}
|
||||
|
||||
- (void)fadeOutEffects
|
||||
{
|
||||
if(self.defaultStyle != SVProgressHUDStyleCustom) {
|
||||
// Remove blur effect
|
||||
self.hudView.effect = nil;
|
||||
}
|
||||
|
||||
// Remove background color
|
||||
self.hudView.backgroundColor = [UIColor clearColor];
|
||||
|
||||
// Fade out views
|
||||
self.backgroundView.alpha = 0.0f;
|
||||
|
||||
self.imageView.alpha = 0.0f;
|
||||
self.statusLabel.alpha = 0.0f;
|
||||
self.indefiniteAnimatedView.alpha = 0.0f;
|
||||
self.ringView.alpha = self.backgroundRingView.alpha = 0.0f;
|
||||
}
|
||||
|
||||
#if TARGET_OS_IOS && __IPHONE_OS_VERSION_MAX_ALLOWED >= 100000
|
||||
- (UINotificationFeedbackGenerator *)hapticGenerator NS_AVAILABLE_IOS(10_0) {
|
||||
// Only return if haptics are enabled
|
||||
if(!self.hapticsEnabled) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
if(!_hapticGenerator) {
|
||||
_hapticGenerator = [[UINotificationFeedbackGenerator alloc] init];
|
||||
}
|
||||
return _hapticGenerator;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#pragma mark - UIAppearance Setters
|
||||
|
||||
- (void)setDefaultStyle:(SVProgressHUDStyle)style {
|
||||
if (!_isInitializing) _defaultStyle = style;
|
||||
}
|
||||
|
||||
- (void)setDefaultMaskType:(SVProgressHUDMaskType)maskType {
|
||||
if (!_isInitializing) _defaultMaskType = maskType;
|
||||
}
|
||||
|
||||
- (void)setDefaultAnimationType:(SVProgressHUDAnimationType)animationType {
|
||||
if (!_isInitializing) _defaultAnimationType = animationType;
|
||||
}
|
||||
|
||||
- (void)setContainerView:(UIView *)containerView {
|
||||
if (!_isInitializing) _containerView = containerView;
|
||||
}
|
||||
|
||||
- (void)setMinimumSize:(CGSize)minimumSize {
|
||||
if (!_isInitializing) _minimumSize = minimumSize;
|
||||
}
|
||||
|
||||
- (void)setRingThickness:(CGFloat)ringThickness {
|
||||
if (!_isInitializing) _ringThickness = ringThickness;
|
||||
}
|
||||
|
||||
- (void)setRingRadius:(CGFloat)ringRadius {
|
||||
if (!_isInitializing) _ringRadius = ringRadius;
|
||||
}
|
||||
|
||||
- (void)setRingNoTextRadius:(CGFloat)ringNoTextRadius {
|
||||
if (!_isInitializing) _ringNoTextRadius = ringNoTextRadius;
|
||||
}
|
||||
|
||||
- (void)setCornerRadius:(CGFloat)cornerRadius {
|
||||
if (!_isInitializing) _cornerRadius = cornerRadius;
|
||||
}
|
||||
|
||||
- (void)setFont:(UIFont*)font {
|
||||
if (!_isInitializing) _font = font;
|
||||
}
|
||||
|
||||
- (void)setForegroundColor:(UIColor*)color {
|
||||
if (!_isInitializing) _foregroundColor = color;
|
||||
}
|
||||
|
||||
- (void)setBackgroundColor:(UIColor*)color {
|
||||
if (!_isInitializing) _backgroundColor = color;
|
||||
}
|
||||
|
||||
- (void)setBackgroundLayerColor:(UIColor*)color {
|
||||
if (!_isInitializing) _backgroundLayerColor = color;
|
||||
}
|
||||
|
||||
- (void)setShouldTintImages:(BOOL)shouldTintImages {
|
||||
if (!_isInitializing) _shouldTintImages = shouldTintImages;
|
||||
}
|
||||
|
||||
- (void)setInfoImage:(UIImage*)image {
|
||||
if (!_isInitializing) _infoImage = image;
|
||||
}
|
||||
|
||||
- (void)setSuccessImage:(UIImage*)image {
|
||||
if (!_isInitializing) _successImage = image;
|
||||
}
|
||||
|
||||
- (void)setErrorImage:(UIImage*)image {
|
||||
if (!_isInitializing) _errorImage = image;
|
||||
}
|
||||
|
||||
- (void)setViewForExtension:(UIView*)view {
|
||||
if (!_isInitializing) _viewForExtension = view;
|
||||
}
|
||||
|
||||
- (void)setOffsetFromCenter:(UIOffset)offset {
|
||||
if (!_isInitializing) _offsetFromCenter = offset;
|
||||
}
|
||||
|
||||
- (void)setMinimumDismissTimeInterval:(NSTimeInterval)minimumDismissTimeInterval {
|
||||
if (!_isInitializing) _minimumDismissTimeInterval = minimumDismissTimeInterval;
|
||||
}
|
||||
|
||||
- (void)setFadeInAnimationDuration:(NSTimeInterval)duration {
|
||||
if (!_isInitializing) _fadeInAnimationDuration = duration;
|
||||
}
|
||||
|
||||
- (void)setFadeOutAnimationDuration:(NSTimeInterval)duration {
|
||||
if (!_isInitializing) _fadeOutAnimationDuration = duration;
|
||||
}
|
||||
|
||||
- (void)setMaxSupportedWindowLevel:(UIWindowLevel)maxSupportedWindowLevel {
|
||||
if (!_isInitializing) _maxSupportedWindowLevel = maxSupportedWindowLevel;
|
||||
}
|
||||
|
||||
@end
|
||||
14
Pods/SVProgressHUD/SVProgressHUD/SVRadialGradientLayer.h
generated
Normal file
@@ -0,0 +1,14 @@
|
||||
//
|
||||
// SVRadialGradientLayer.h
|
||||
// SVProgressHUD, https://github.com/SVProgressHUD/SVProgressHUD
|
||||
//
|
||||
// Copyright (c) 2014-2018 Tobias Tiemerding. All rights reserved.
|
||||
//
|
||||
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
|
||||
@interface SVRadialGradientLayer : CALayer
|
||||
|
||||
@property (nonatomic) CGPoint gradientCenter;
|
||||
|
||||
@end
|
||||
25
Pods/SVProgressHUD/SVProgressHUD/SVRadialGradientLayer.m
generated
Normal file
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// SVRadialGradientLayer.m
|
||||
// SVProgressHUD, https://github.com/SVProgressHUD/SVProgressHUD
|
||||
//
|
||||
// Copyright (c) 2014-2018 Tobias Tiemerding. All rights reserved.
|
||||
//
|
||||
|
||||
#import "SVRadialGradientLayer.h"
|
||||
|
||||
@implementation SVRadialGradientLayer
|
||||
|
||||
- (void)drawInContext:(CGContextRef)context {
|
||||
size_t locationsCount = 2;
|
||||
CGFloat locations[2] = {0.0f, 1.0f};
|
||||
CGFloat colors[8] = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.75f};
|
||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, colors, locations, locationsCount);
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
|
||||
float radius = MIN(self.bounds.size.width , self.bounds.size.height);
|
||||
CGContextDrawRadialGradient (context, gradient, self.gradientCenter, 0, self.gradientCenter, radius, kCGGradientDrawsAfterEndLocation);
|
||||
CGGradientRelease(gradient);
|
||||
}
|
||||
|
||||
@end
|
||||