1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546 |
- //
- // SVProgressHUD.h
- // SVProgressHUD, https://github.com/SVProgressHUD/SVProgressHUD
- //
- // Copyright (c) 2011-2019 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) UIBlurEffect *hudViewCustomBlurEffect;
- @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)setForegroundImageColor:(UIColor *)color {
- [self sharedView].foregroundImageColor = color;
- [self setDefaultStyle:SVProgressHUDStyleCustom];
- }
- + (void)setBackgroundColor:(UIColor*)color {
- [self sharedView].backgroundColor = color;
- [self setDefaultStyle:SVProgressHUDStyleCustom];
- }
- + (void)setHudViewCustomBlurEffect:(UIBlurEffect*)blurEffect {
- [self sharedView].hudViewCustomBlurEffect = blurEffect;
- [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;
- }
- + (void)setMotionEffectEnabled:(BOOL)motionEffectEnabled {
- [self sharedView].motionEffectEnabled = motionEffectEnabled;
- }
- #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;
- _motionEffectEnabled = YES;
-
- // 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 (_motionEffectEnabled) {
- #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.foregroundImageColorForStyle;
- } 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
- // \n cause to read text twice so remove "\n" new line character before setting up accessiblity label
- NSString *accessibilityString = [[self.statusLabel.text componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]] componentsJoinedByString:@" "];
- if(self.defaultMaskType != SVProgressHUDMaskTypeNone) {
- self.controlView.userInteractionEnabled = YES;
- self.accessibilityLabel = accessibilityString ?: NSLocalizedString(@"Loading", nil);
- self.isAccessibilityElement = YES;
- self.controlView.accessibilityViewIsModal = YES;
- } else {
- self.controlView.userInteractionEnabled = NO;
- self.hudView.accessibilityLabel = accessibilityString ?: NSLocalizedString(@"Loading", nil);
- self.hudView.isAccessibilityElement = YES;
- self.controlView.accessibilityViewIsModal = NO;
- }
-
- // 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){
-
- // 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(), ^{
-
- // Stop timer
- strongSelf.graceTimer = nil;
-
- 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.foregroundImageColorForStyle;
- 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.foregroundImageColorForStyle;
- }
- [_indefiniteAnimatedView sizeToFit];
-
- return _indefiniteAnimatedView;
- }
- - (SVProgressAnimatedView*)ringView {
- if(!_ringView) {
- _ringView = [[SVProgressAnimatedView alloc] initWithFrame:CGRectZero];
- }
-
- // Update styling
- _ringView.strokeColor = self.foregroundImageColorForStyle;
- _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.foregroundImageColorForStyle 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*)foregroundImageColorForStyle {
- if (self.foregroundImageColor) {
- return self.foregroundImageColor;
- } else {
- return [self foregroundColorForStyle];
- }
- }
- - (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.effect = self.hudViewCustomBlurEffect;
- 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)setForegroundImageColor:(UIColor *)color {
- if (!_isInitializing) _foregroundImageColor = 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
|