iOS 状态栏上的小菊花是由 UIApplicationnetworkActivityIndicatorVisible 这个属性来控制的,将它设置为 YES,菊花就会出现,将它设置为 NO,菊花就会消失。

但这在有多个网络请求时变成了一个很微妙的事情,假设有如下代码:

- (void)doSomething {
    [self updateSomeData];
    [self updateOtherData];
}

- (void)updateSomeData {
    [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
    // 一个异步请求,假设在主线程回调。
    [self.manager fetchSomeDataFromNetwork:^{
        [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
        // ...
    }];
}

- (void)updateOtherData {
    [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
    // 同样也是一个异步请求,假设在主线程回调。
    [self.manager fetchOtherDataFromNetwork:^{
        [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
        // ...
    }];
}

先返回的 block 直接就把小菊花给关掉了,这样小菊花的显示状态就不对了。

解决这个问题,可以借鉴 Objective-C 中的引用计数:

  • 给小菊花一个计数值,就像引用计数一样;
  • 每当 networkActivityIndicatorVisible 被赋值为 YES 时,给计数值加 1,像 retain 一样;
  • 每当 networkActivityIndicatorVisible 被赋值为 NO 时,给计数值减 1,像 release 一样;
  • 小菊花的计数值大于 0 时,则让小菊花显现,否则让小菊花消失。

实现

可以通过给 UIApplication 新增一个 category 来实现,将 setNetworkActivityIndicatorVisible: 方法和下面的方法交换(Method Swizzling)即可:

- (void)kr_setNetworkActivityIndicatorVisible:(BOOL)visible {
    NSUInteger count = [self kr_count];
    if (visible) {
        count++;
    } else if (count > 0) {
        count--;
    }
    [self kr_setNetworkActivityIndicatorVisible:count > 0 ? YES : NO];
    [self kr_setCount:count];
}

完整的代码可以在这里找到。

这样正常的使用 setNetworkActivityIndicatorVisible: 就能让小菊花的显示正确了,上面的代码中的问题也得到了解决。

当然,这个代码肯定是不完善的,对溢出没有做处理,也不是线程安全的(最好都在主线程调用)。但是普通使用应该是足够了的。