Ben Copsey

Stop flickering in iOS network activity indicator by introducing a small delay before hiding it

Based on an idea from Jesse Rusak -> http://github.com/MindSea/asi-http-request/commit/f346f7f9de333501f732404323e8f73e911fbeea
@@ -745,9 +745,15 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount; @@ -745,9 +745,15 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount;
745 #pragma mark network activity 745 #pragma mark network activity
746 746
747 + (BOOL)isNetworkInUse; 747 + (BOOL)isNetworkInUse;
748 -#if TARGET_OS_IPHONE 748 +
749 + (void)setShouldUpdateNetworkActivityIndicator:(BOOL)shouldUpdate; 749 + (void)setShouldUpdateNetworkActivityIndicator:(BOOL)shouldUpdate;
750 -#endif 750 +
  751 +// Shows the network activity spinner thing on iOS. You may wish to override this to do something else in Mac projects
  752 ++ (void)showNetworkActivityIndicator;
  753 +
  754 +// Hides the network activity spinner thing on iOS
  755 ++ (void)hideNetworkActivityIndicator;
  756 +
751 757
752 #pragma mark miscellany 758 #pragma mark miscellany
753 759
@@ -24,7 +24,7 @@ @@ -24,7 +24,7 @@
24 #import "ASIDataCompressor.h" 24 #import "ASIDataCompressor.h"
25 25
26 // Automatically set on build 26 // Automatically set on build
27 -NSString *ASIHTTPRequestVersion = @"v1.7-45 2010-08-18"; 27 +NSString *ASIHTTPRequestVersion = @"v1.7-53 2010-08-18";
28 28
29 NSString* const NetworkRequestErrorDomain = @"ASIHTTPRequestErrorDomain"; 29 NSString* const NetworkRequestErrorDomain = @"ASIHTTPRequestErrorDomain";
30 30
@@ -131,10 +131,12 @@ static id <ASICacheDelegate> defaultCache = nil; @@ -131,10 +131,12 @@ static id <ASICacheDelegate> defaultCache = nil;
131 // Used for tracking when requests are using the network 131 // Used for tracking when requests are using the network
132 static unsigned int runningRequestCount = 0; 132 static unsigned int runningRequestCount = 0;
133 133
134 -#if TARGET_OS_IPHONE 134 +
135 -// Use [ASIHTTPRequest setShouldUpdateNetworkActivityIndicator:NO] if you want to manage it yourself 135 +// You can use [ASIHTTPRequest setShouldUpdateNetworkActivityIndicator:NO] if you want to manage it yourself
  136 +// Alternatively, override showNetworkActivityIndicator / hideNetworkActivityIndicator
  137 +// By default this does nothing on Mac OS X, but again override the above methods for a different behaviour
136 static BOOL shouldUpdateNetworkActivityIndicator = YES; 138 static BOOL shouldUpdateNetworkActivityIndicator = YES;
137 -#endif 139 +
138 140
139 //**Queue stuff**/ 141 //**Queue stuff**/
140 142
@@ -3046,11 +3048,12 @@ static NSOperationQueue *sharedQueue = nil; @@ -3046,11 +3048,12 @@ static NSOperationQueue *sharedQueue = nil;
3046 3048
3047 if ([self readStreamIsScheduled]) { 3049 if ([self readStreamIsScheduled]) {
3048 runningRequestCount--; 3050 runningRequestCount--;
3049 - #if TARGET_OS_IPHONE  
3050 if (shouldUpdateNetworkActivityIndicator && runningRequestCount == 0) { 3051 if (shouldUpdateNetworkActivityIndicator && runningRequestCount == 0) {
3051 - [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; 3052 + // Wait half a second before turning off the indicator
  3053 + // This can prevent flicker when you have a single request finish and then immediately start another request
  3054 + // We will cancel hiding the activity indicator if we start again
  3055 + [[self class] performSelector:@selector(hideNetworkActivityIndicator) withObject:nil afterDelay:0.5];
3052 } 3056 }
3053 - #endif  
3054 } 3057 }
3055 3058
3056 [self setReadStreamIsScheduled:NO]; 3059 [self setReadStreamIsScheduled:NO];
@@ -3070,11 +3073,10 @@ static NSOperationQueue *sharedQueue = nil; @@ -3070,11 +3073,10 @@ static NSOperationQueue *sharedQueue = nil;
3070 3073
3071 [connectionsLock lock]; 3074 [connectionsLock lock];
3072 runningRequestCount++; 3075 runningRequestCount++;
3073 - #if TARGET_OS_IPHONE  
3074 if (shouldUpdateNetworkActivityIndicator) { 3076 if (shouldUpdateNetworkActivityIndicator) {
3075 - [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; 3077 + [NSObject cancelPreviousPerformRequestsWithTarget:[self class] selector:@selector(hideNetworkActivityIndicator) object:nil];
  3078 + [[self class] showNetworkActivityIndicator];
3076 } 3079 }
3077 - #endif  
3078 [connectionsLock unlock]; 3080 [connectionsLock unlock];
3079 3081
3080 // Reset the timeout 3082 // Reset the timeout
@@ -3084,17 +3086,19 @@ static NSOperationQueue *sharedQueue = nil; @@ -3084,17 +3086,19 @@ static NSOperationQueue *sharedQueue = nil;
3084 } 3086 }
3085 } 3087 }
3086 3088
  3089 +
3087 - (void)unscheduleReadStream 3090 - (void)unscheduleReadStream
3088 { 3091 {
3089 if ([self readStream] && [self readStreamIsScheduled]) { 3092 if ([self readStream] && [self readStreamIsScheduled]) {
3090 3093
3091 [connectionsLock lock]; 3094 [connectionsLock lock];
3092 runningRequestCount--; 3095 runningRequestCount--;
3093 - #if TARGET_OS_IPHONE  
3094 if (shouldUpdateNetworkActivityIndicator && runningRequestCount == 0) { 3096 if (shouldUpdateNetworkActivityIndicator && runningRequestCount == 0) {
3095 - [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; 3097 + // Wait half a second before turning off the indicator
  3098 + // This can prevent flicker when you have a single request finish and then immediately start another request
  3099 + // We will cancel hiding the activity indicator if we start again
  3100 + [[self class] performSelector:@selector(hideNetworkActivityIndicator) withObject:nil afterDelay:0.5];
3096 } 3101 }
3097 - #endif  
3098 [connectionsLock unlock]; 3102 [connectionsLock unlock];
3099 3103
3100 [[self readStream] removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:[self runLoopMode]]; 3104 [[self readStream] removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:[self runLoopMode]];
@@ -3825,14 +3829,27 @@ static NSOperationQueue *sharedQueue = nil; @@ -3825,14 +3829,27 @@ static NSOperationQueue *sharedQueue = nil;
3825 [connectionsLock unlock]; 3829 [connectionsLock unlock];
3826 return inUse; 3830 return inUse;
3827 } 3831 }
3828 -#if TARGET_OS_IPHONE 3832 +
3829 + (void)setShouldUpdateNetworkActivityIndicator:(BOOL)shouldUpdate 3833 + (void)setShouldUpdateNetworkActivityIndicator:(BOOL)shouldUpdate
3830 { 3834 {
3831 [connectionsLock lock]; 3835 [connectionsLock lock];
3832 shouldUpdateNetworkActivityIndicator = shouldUpdate; 3836 shouldUpdateNetworkActivityIndicator = shouldUpdate;
3833 [connectionsLock unlock]; 3837 [connectionsLock unlock];
3834 } 3838 }
  3839 +
  3840 ++ (void)showNetworkActivityIndicator
  3841 +{
  3842 +#if TARGET_OS_IPHONE
  3843 + [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
3835 #endif 3844 #endif
  3845 +}
  3846 +
  3847 ++ (void)hideNetworkActivityIndicator
  3848 +{
  3849 +#if TARGET_OS_IPHONE
  3850 + [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
  3851 +#endif
  3852 +}
3836 3853
3837 3854
3838 #pragma mark threading behaviour 3855 #pragma mark threading behaviour