如何同时用数据和 运行 一个 activity 指示器填充 pickerview?

How to fill a pickerview with data and run an activity indicator at the same time?

我有一个方法可以从 Web 服务加载数据并填充数组,它工作正常(我们称之为 "get_data")。我使用此数组 ("arr") 作为选择器视图 ("pv") 的数据源,它也工作正常,当我 运行 我的应用程序时,选择器视图充满了项目。有一天我决定使用一个 "activity indicator" ("ai"),现在 "pv" 总是空的,我不知道如何修复它。接下来是我的代码:

- (void) viewDidLoad {
[super viewDidLoad];
[ai startAnimation]; // ACTIVITY INDICATOR STARTS SPINNING.
[self performSelector:@selector( get_data )  // FILL ARRAY.
           withObject:nil
           afterDelay:0]; }
//-------------------------------------------------------------------
- (void) get_data { // CALL WEB SERVICE, FILL ARRAY WITH JSON DATA.
[ai stopAnimation]; }  // SPINNING STOPS AFTER LONG TASK.
//-------------------------------------------------------------------
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
return 1; }
//-------------------------------------------------------------------
- (NSInteger)pickerView :(UIPickerView *)pickerView 
 numberOfRowsInComponent:(NSInteger)component {
return [arr count]; }
//-------------------------------------------------------------------
- (NSString *)pickerView:(UIPickerView *)pickerView
             titleForRow:(NSInteger)row
            forComponent:(NSInteger)component {
return [arr objectAtIndex:row]; }
//-------------------------------------------------------------------
- (void) pickerView:(UIPickerView *)pickerView
       didSelectRow:(NSInteger )row
        inComponent:(NSInteger )component {
index = [pv selectedRowInComponent:0]; } // INDEX IS A GLOBAL INT.
//-------------------------------------------------------------------
- ( NSAttributedString * ) pickerView:(UIPickerView *)pickerView
                attributedTitleForRow:(NSInteger)row
                         forComponent:(NSInteger)component {
NSString * title = [arr objectAtIndex:row];
NSAttributedString * attString = [[NSAttributedString alloc]
                                 initWithString:title attributes:
                                 @{NSForegroundColorAttributeName:
                                 [UIColor blackColor]}];
return attString; }

我之前用 "activity indicators" 和 "performSelector" 使用过,它们运行良好。我不明白的是为什么 pickerview 方法不再适用于数组,所以,我的问题是:如何同时用数据和 运行 一个 activity 指示器填充一个 pickerview ?

如果我像这样更改我的 viewDidLoad,pickerview 会工作,但 activity 指示器不会:

- (void) viewDidLoad {
[super viewDidLoad];
/* [ai startAnimation]; // ACTIVITY INDICATOR STARTS SPINNING.
[self performSelector:@selector( get_data )  // FILL ARRAY.
           withObject:nil
           afterDelay:0]; */
[self get_data];
}

当您使用 performSelector: 时,它会在同一个线程上执行,就像您直接调用选择器一样。

- (void) viewDidLoad {
  [super viewDidLoad];
  [ai startAnimation]; // ACTIVITY INDICATOR STARTS SPINNING.
  [self performSelector:@selector( get_data )  // FILL ARRAY.
             withObject:nil
             afterDelay:0];
}

- (void) get_data {
  // CALL WEB SERVICE, FILL ARRAY WITH JSON DATA.
  [ai stopAnimation];  // SPINNING STOPS AFTER LONG TASK.
}

这等同于:

- (void) viewDidLoad {
  [super viewDidLoad];
  [ai startAnimation]; // ACTIVITY INDICATOR STARTS SPINNING.
  [self get_data];  // FILL ARRAY.
}

或简单地:

- (void) viewDidLoad {
  [super viewDidLoad];
  [ai startAnimation]; // ACTIVITY INDICATOR STARTS SPINNING.
  // CALL WEB SERVICE, FILL ARRAY WITH JSON DATA.
  [ai stopAnimation]; // SPINNING STOPS AFTER LONG TASK.
}

如果您的 Web 服务在不同的线程或块中执行并允许您附加完成响应程序,您应该将 [ai stopAnimation]; 放在那里。

如果不是这种情况,并且您正在主线程上同步调用 Web 服务以获取数据(永远不要在主线程上执行阻塞网络操作) ,您可以改为将其全部转移到后台线程:

- (void) viewDidLoad {
  [super viewDidLoad];
  [ai startAnimation]; // ACTIVITY INDICATOR STARTS SPINNING.
  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0L), ^{
  // CALL WEB SERVICE, FILL ARRAY WITH JSON DATA.
  dispatch_async(dispatch_get_main_queue(), ^{
    [ai stopAnimations]; // SPINNING STOPS AFTER LONG TASK.
  });
});

确保已设置选择器视图的 dataSourcedelegate。 Web 服务完成后,您需要使用更新后的 arr.

重新加载选择器的数据源
- (void) viewDidLoad {
[super viewDidLoad];
pv.dataSource = self;
pv.delegate = self;
[ai startAnimation]; // ACTIVITY INDICATOR STARTS SPINNING.
[self performSelector:@selector( get_data )  // FILL ARRAY.
           withObject:nil
           afterDelay:0]; }

- (void) get_data {
  // CALL WEB SERVICE, FILL ARRAY WITH JSON DATA.
  [ai stopAnimation];  // SPINNING STOPS AFTER LONG TASK.
  [pv reloadAllComponents];
}