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
@@ -725,9 +725,15 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount; @@ -725,9 +725,15 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount;
725 #pragma mark network activity 725 #pragma mark network activity
726 726
727 + (BOOL)isNetworkInUse; 727 + (BOOL)isNetworkInUse;
728 -#if TARGET_OS_IPHONE 728 +
729 + (void)setShouldUpdateNetworkActivityIndicator:(BOOL)shouldUpdate; 729 + (void)setShouldUpdateNetworkActivityIndicator:(BOOL)shouldUpdate;
730 -#endif 730 +
  731 +// Shows the network activity spinner thing on iOS. You may wish to override this to do something else in Mac projects
  732 ++ (void)showNetworkActivityIndicator;
  733 +
  734 +// Hides the network activity spinner thing on iOS
  735 ++ (void)hideNetworkActivityIndicator;
  736 +
731 737
732 #pragma mark miscellany 738 #pragma mark miscellany
733 739
@@ -23,7 +23,7 @@ @@ -23,7 +23,7 @@
23 23
24 24
25 // Automatically set on build 25 // Automatically set on build
26 -NSString *ASIHTTPRequestVersion = @"v1.7-42 2010-08-18"; 26 +NSString *ASIHTTPRequestVersion = @"v1.7-53 2010-08-18";
27 27
28 NSString* const NetworkRequestErrorDomain = @"ASIHTTPRequestErrorDomain"; 28 NSString* const NetworkRequestErrorDomain = @"ASIHTTPRequestErrorDomain";
29 29
@@ -130,10 +130,12 @@ static id <ASICacheDelegate> defaultCache = nil; @@ -130,10 +130,12 @@ static id <ASICacheDelegate> defaultCache = nil;
130 // Used for tracking when requests are using the network 130 // Used for tracking when requests are using the network
131 static unsigned int runningRequestCount = 0; 131 static unsigned int runningRequestCount = 0;
132 132
133 -#if TARGET_OS_IPHONE 133 +
134 -// Use [ASIHTTPRequest setShouldUpdateNetworkActivityIndicator:NO] if you want to manage it yourself 134 +// You can use [ASIHTTPRequest setShouldUpdateNetworkActivityIndicator:NO] if you want to manage it yourself
  135 +// Alternatively, override showNetworkActivityIndicator / hideNetworkActivityIndicator
  136 +// By default this does nothing on Mac OS X, but again override the above methods for a different behaviour
135 static BOOL shouldUpdateNetworkActivityIndicator = YES; 137 static BOOL shouldUpdateNetworkActivityIndicator = YES;
136 -#endif 138 +
137 139
138 //**Queue stuff**/ 140 //**Queue stuff**/
139 141
@@ -2990,11 +2992,12 @@ static NSOperationQueue *sharedQueue = nil; @@ -2990,11 +2992,12 @@ static NSOperationQueue *sharedQueue = nil;
2990 2992
2991 if ([self readStreamIsScheduled]) { 2993 if ([self readStreamIsScheduled]) {
2992 runningRequestCount--; 2994 runningRequestCount--;
2993 - #if TARGET_OS_IPHONE  
2994 if (shouldUpdateNetworkActivityIndicator && runningRequestCount == 0) { 2995 if (shouldUpdateNetworkActivityIndicator && runningRequestCount == 0) {
2995 - [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; 2996 + // Wait half a second before turning off the indicator
  2997 + // This can prevent flicker when you have a single request finish and then immediately start another request
  2998 + // We will cancel hiding the activity indicator if we start again
  2999 + [[self class] performSelector:@selector(hideNetworkActivityIndicator) withObject:nil afterDelay:0.5];
2996 } 3000 }
2997 - #endif  
2998 } 3001 }
2999 3002
3000 [self setReadStreamIsScheduled:NO]; 3003 [self setReadStreamIsScheduled:NO];
@@ -3014,11 +3017,10 @@ static NSOperationQueue *sharedQueue = nil; @@ -3014,11 +3017,10 @@ static NSOperationQueue *sharedQueue = nil;
3014 3017
3015 [connectionsLock lock]; 3018 [connectionsLock lock];
3016 runningRequestCount++; 3019 runningRequestCount++;
3017 - #if TARGET_OS_IPHONE  
3018 if (shouldUpdateNetworkActivityIndicator) { 3020 if (shouldUpdateNetworkActivityIndicator) {
3019 - [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; 3021 + [NSObject cancelPreviousPerformRequestsWithTarget:[self class] selector:@selector(hideNetworkActivityIndicator) object:nil];
  3022 + [[self class] showNetworkActivityIndicator];
3020 } 3023 }
3021 - #endif  
3022 [connectionsLock unlock]; 3024 [connectionsLock unlock];
3023 3025
3024 // Reset the timeout 3026 // Reset the timeout
@@ -3028,17 +3030,19 @@ static NSOperationQueue *sharedQueue = nil; @@ -3028,17 +3030,19 @@ static NSOperationQueue *sharedQueue = nil;
3028 } 3030 }
3029 } 3031 }
3030 3032
  3033 +
3031 - (void)unscheduleReadStream 3034 - (void)unscheduleReadStream
3032 { 3035 {
3033 if ([self readStream] && [self readStreamIsScheduled]) { 3036 if ([self readStream] && [self readStreamIsScheduled]) {
3034 3037
3035 [connectionsLock lock]; 3038 [connectionsLock lock];
3036 runningRequestCount--; 3039 runningRequestCount--;
3037 - #if TARGET_OS_IPHONE  
3038 if (shouldUpdateNetworkActivityIndicator && runningRequestCount == 0) { 3040 if (shouldUpdateNetworkActivityIndicator && runningRequestCount == 0) {
3039 - [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; 3041 + // Wait half a second before turning off the indicator
  3042 + // This can prevent flicker when you have a single request finish and then immediately start another request
  3043 + // We will cancel hiding the activity indicator if we start again
  3044 + [[self class] performSelector:@selector(hideNetworkActivityIndicator) withObject:nil afterDelay:0.5];
3040 } 3045 }
3041 - #endif  
3042 [connectionsLock unlock]; 3046 [connectionsLock unlock];
3043 3047
3044 [[self readStream] removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:[self runLoopMode]]; 3048 [[self readStream] removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:[self runLoopMode]];
@@ -3976,14 +3980,27 @@ static NSOperationQueue *sharedQueue = nil; @@ -3976,14 +3980,27 @@ static NSOperationQueue *sharedQueue = nil;
3976 [connectionsLock unlock]; 3980 [connectionsLock unlock];
3977 return inUse; 3981 return inUse;
3978 } 3982 }
3979 -#if TARGET_OS_IPHONE 3983 +
3980 + (void)setShouldUpdateNetworkActivityIndicator:(BOOL)shouldUpdate 3984 + (void)setShouldUpdateNetworkActivityIndicator:(BOOL)shouldUpdate
3981 { 3985 {
3982 [connectionsLock lock]; 3986 [connectionsLock lock];
3983 shouldUpdateNetworkActivityIndicator = shouldUpdate; 3987 shouldUpdateNetworkActivityIndicator = shouldUpdate;
3984 [connectionsLock unlock]; 3988 [connectionsLock unlock];
3985 } 3989 }
  3990 +
  3991 ++ (void)showNetworkActivityIndicator
  3992 +{
  3993 +#if TARGET_OS_IPHONE
  3994 + [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
3986 #endif 3995 #endif
  3996 +}
  3997 +
  3998 ++ (void)hideNetworkActivityIndicator
  3999 +{
  4000 +#if TARGET_OS_IPHONE
  4001 + [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
  4002 +#endif
  4003 +}
3987 4004
3988 4005
3989 #pragma mark threading behaviour 4006 #pragma mark threading behaviour