diff --git a/CHANGELOG.mdown b/CHANGELOG.mdown index d502263..2c8a6af 100644 --- a/CHANGELOG.mdown +++ b/CHANGELOG.mdown @@ -1,3 +1,7 @@ +**master** + + - Added support for `NSProgress` + **Version 0.9.2** @ 22.12.15 - Timer improvements diff --git a/Demo/Classes/MBHudDemoViewController.m b/Demo/Classes/MBHudDemoViewController.m index 606340f..2f93119 100644 --- a/Demo/Classes/MBHudDemoViewController.m +++ b/Demo/Classes/MBHudDemoViewController.m @@ -8,7 +8,7 @@ #import "MBHudDemoViewController.h" #import "MBProgressHUD.h" - +#import @interface MBExample : NSObject @@ -33,7 +33,7 @@ @interface MBHudDemoViewController () @property (nonatomic, strong) NSArray *> *examples; -@property (nonatomic, assign) BOOL canceled; +@property (atomic, assign) BOOL canceled; // atomic, because it may be cancelled from main thread, flag is read on a background thread @end @@ -57,6 +57,7 @@ [MBExample exampleWithTitle:@"Mode switching" selector:@selector(modeSwitchingExample)]], @[[MBExample exampleWithTitle:@"On window" selector:@selector(indeterminateExample)], [MBExample exampleWithTitle:@"NSURLSession" selector:@selector(networkingExample)], + [MBExample exampleWithTitle:@"Determinate with NSProgress" selector:@selector(determinateNSProgressExample)], [MBExample exampleWithTitle:@"Dim background" selector:@selector(indeterminateExample)], [MBExample exampleWithTitle:@"Colored" selector:@selector(indeterminateExample)]] ]; @@ -143,6 +144,26 @@ }); } +- (void)determinateNSProgressExample { + MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.navigationController.view animated:YES]; + + // Set the determinate mode to show task progress. + hud.mode = MBProgressHUDModeDeterminate; + hud.label.text = NSLocalizedString(@"Loading...", @"HUD loading title"); + + NSProgress *progressObject = [NSProgress progressWithTotalUnitCount:200000]; + + hud.progressObject = progressObject; + + dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{ + // Do something useful in the background and update the HUD periodically. + [self doSomeWorkWithProgressObject:progressObject]; + dispatch_async(dispatch_get_main_queue(), ^{ + [hud hideAnimated:YES]; + }); + }); +} + - (void)annularDeterminateExample { MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.navigationController.view animated:YES]; @@ -258,6 +279,16 @@ sleep(3.); } +- (void)doSomeWorkWithProgressObject:(NSProgress *)progressObject { + self.canceled = NO; + // This just increases the progress indicator in a loop. + while (progressObject.fractionCompleted < 1.0f) { + if (self.canceled) break; + [progressObject becomeCurrentWithPendingUnitCount:1]; + [progressObject resignCurrent]; + } +} + - (void)doSomeWorkWithProgress { self.canceled = NO; // This just increases the progress indicator in a loop. diff --git a/Demo/HudDemo.xcodeproj/project.pbxproj b/Demo/HudDemo.xcodeproj/project.pbxproj index 46034fb..891457f 100644 --- a/Demo/HudDemo.xcodeproj/project.pbxproj +++ b/Demo/HudDemo.xcodeproj/project.pbxproj @@ -12,6 +12,7 @@ 1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D30AB110D05D00D00671497 /* Foundation.framework */; }; 1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */; }; 28D7ACF80DDB3853001CB0EB /* MBHudDemoViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 28D7ACF70DDB3853001CB0EB /* MBHudDemoViewController.m */; }; + 5BE141EE1CD8C33D0023BC9E /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5BE141ED1CD8C33D0023BC9E /* QuartzCore.framework */; }; D22F7D810F85241C00550BB3 /* MBProgressHUD.m in Sources */ = {isa = PBXBuildFile; fileRef = D22F7D800F85241C00550BB3 /* MBProgressHUD.m */; }; D286A76D1518CA9F00E13FB8 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D286A76C1518CA9F00E13FB8 /* CoreGraphics.framework */; }; D2B174121C5E3A3D00021915 /* HudTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D2B174111C5E3A3D00021915 /* HudTests.m */; }; @@ -40,6 +41,7 @@ 28D7ACF70DDB3853001CB0EB /* MBHudDemoViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MBHudDemoViewController.m; sourceTree = ""; usesTabs = 1; }; 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 32CA4F630368D1EE00C91783 /* HudDemo_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HudDemo_Prefix.pch; sourceTree = ""; }; + 5BE141ED1CD8C33D0023BC9E /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; D22F7D7F0F85241C00550BB3 /* MBProgressHUD.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MBProgressHUD.h; path = ../MBProgressHUD.h; sourceTree = SOURCE_ROOT; }; D22F7D800F85241C00550BB3 /* MBProgressHUD.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MBProgressHUD.m; path = ../MBProgressHUD.m; sourceTree = ""; }; @@ -57,6 +59,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 5BE141EE1CD8C33D0023BC9E /* QuartzCore.framework in Frameworks */, D286A76D1518CA9F00E13FB8 /* CoreGraphics.framework in Frameworks */, 1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */, 1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */, @@ -132,6 +135,7 @@ 29B97323FDCFA39411CA2CEA /* Frameworks */ = { isa = PBXGroup; children = ( + 5BE141ED1CD8C33D0023BC9E /* QuartzCore.framework */, D286A76C1518CA9F00E13FB8 /* CoreGraphics.framework */, 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */, 1D30AB110D05D00D00671497 /* Foundation.framework */, @@ -488,6 +492,7 @@ D2B174181C5E3A3D00021915 /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; diff --git a/MBProgressHUD.h b/MBProgressHUD.h index 964d41f..c68f993 100644 --- a/MBProgressHUD.h +++ b/MBProgressHUD.h @@ -254,6 +254,13 @@ NS_ASSUME_NONNULL_BEGIN */ @property (assign, nonatomic) float progress; +/// @name ProgressObject + +/** + * The NSProgress object feeding the progress information to the progress indicator. + */ +@property (strong, nonatomic, nullable) NSProgress *progressObject; + /// @name Views /** diff --git a/MBProgressHUD.m b/MBProgressHUD.m index cf8bae3c..d944f91 100644 --- a/MBProgressHUD.m +++ b/MBProgressHUD.m @@ -42,6 +42,7 @@ static const CGFloat MBDefaultDetailsLabelFontSize = 12.f; @property (nonatomic, weak) NSTimer *graceTimer; @property (nonatomic, weak) NSTimer *minShowTimer; @property (nonatomic, weak) NSTimer *hideDelayTimer; +@property (nonatomic, weak) CADisplayLink *progressObjectDisplayLink; // Deprecated @property (copy, nullable) MBProgressHUDCompletionBlock completionBlock; @@ -156,6 +157,7 @@ static const CGFloat MBDefaultDetailsLabelFontSize = 12.f; - (void)hideAnimated:(BOOL)animated { MBMainThreadAssert(); + self.progressObjectDisplayLink = nil; [self.graceTimer invalidate]; self.useAnimation = animated; self.finished = YES; @@ -652,6 +654,32 @@ static const CGFloat MBDefaultDetailsLabelFontSize = 12.f; } } +- (void)setProgressObjectDisplayLink:(CADisplayLink *)progressObjectDisplayLink { + if (progressObjectDisplayLink != _progressObjectDisplayLink) { + [_progressObjectDisplayLink invalidate]; + + _progressObjectDisplayLink = progressObjectDisplayLink; + + [_progressObjectDisplayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; + } +} + +- (void)setProgressObject:(NSProgress *)progressObject { + if (progressObject != _progressObject) { + _progressObject = progressObject; + + // We're using CADisplayLink, because NSProgress can change very quickly and observing it may starve the main thread, + // so we're refreshing the progress only every frame draw + self.progressObjectDisplayLink = [CADisplayLink displayLinkWithTarget:self + selector:@selector(updateProgressFromProgressObject)]; + + } +} + +- (void)updateProgressFromProgressObject { + self.progress = self.progressObject.fractionCompleted; +} + - (void)setProgress:(float)progress { if (progress != _progress) { _progress = progress; diff --git a/MBProgressHUD.podspec b/MBProgressHUD.podspec index 48f2376..3f1a0ef 100644 --- a/MBProgressHUD.podspec +++ b/MBProgressHUD.podspec @@ -15,6 +15,6 @@ Pod::Spec.new do |s| s.ios.deployment_target = '6.0' s.tvos.deployment_target = '9.0' s.source_files = '*.{h,m}' - s.framework = "CoreGraphics" + s.frameworks = "CoreGraphics", "QuartzCore" s.requires_arc = true end diff --git a/README.mdown b/README.mdown index 9285024..00199c6 100644 --- a/README.mdown +++ b/README.mdown @@ -87,6 +87,18 @@ hud.labelText = @"Loading"; }]; ``` +You can also use a `NSProgress` object and MBProgressHUD will update itself when there is progress reported through that object. + +```objective-c +MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES]; +hud.mode = MBProgressHUDModeAnnularDeterminate; +hud.labelText = @"Loading"; +NSProgress *progress = [self doSomethingInBackgroundCompletion:^{ + [hud hide:YES]; +}]; +hud.progressObject = progress; +``` + UI updates should always be done on the main thread. Some MBProgressHUD setters are however considered "thread safe" and can be called from background threads. Those also include `setMode:`, `setCustomView:`, `setLabelText:`, `setLabelFont:`, `setDetailsLabelText:`, `setDetailsLabelFont:` and `setProgress:`. If you need to run your long-running task in the main thread, you should perform it with a slight delay, so UIKit will have enough time to update the UI (i.e., draw the HUD) before you block the main thread with your task.