123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509 |
- //
- // 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
|