【问题标题】:How to display UIActivityIndicatorView while fetching JSON data asynchronously to be populated in UITableView?如何在异步获取 JSON 数据以填充到 UITableView 中时显示 UIActivityIndi​​catorView?
【发布时间】:2014-09-14 19:08:27
【问题描述】:

我正在调用一个 GET API,它接受一个字符串关键字并返回我的 JSON 数据,我解析并显示在我的 UITableView 中

当 API 返回数据时,我正在显示 UIActivityIndi​​catorView,这工作正常。 但是,一旦收到数据,UIActivityIndi​​catorView 就会按预期消失,但数据不会显示在 UITableView 中,但是如果我触摸屏幕上的任何位置,数据就会在 UI TableView 中可见。

这是我的代码:

-(void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
{
    [indicator startAnimating];
    indicator.hidesWhenStopped = YES;

    dispatch_queue_t queue = dispatch_queue_create("ID", NULL);
    dispatch_async(queue, ^{
        NSString *searchText=searchBar.text;
        NSString *trimmedString = [searchText stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceCharacterSet]];
        if (trimmedString.length==0) {
            isFilter=NO;

            UIAlertView *noConn = [[UIAlertView alloc] initWithTitle:@"ERROR" message:@"Please enter something in search bar" delegate:self cancelButtonTitle:nil otherButtonTitles:@"ok", nil];
            [noConn show];
        } else {
            NSString *searchNew = [trimmedString stringByReplacingOccurrencesOfString:@" " withString:@"%20"];
            isFilter=YES;
            @try {
                [label removeFromSuperview];
                _Title1 = [[NSMutableArray alloc] init];
                _Author1 = [[NSMutableArray alloc] init];
                _Images1 = [[NSMutableArray alloc] init];
                _Details1 = [[NSMutableArray alloc] init];
                _link1 = [[NSMutableArray alloc] init];
                _Date1 = [[NSMutableArray alloc] init];
                NSString* myURLString = [NSString stringWithFormat:@"www.example.com=%@", searchNew];
                NSURL *url = [NSURL URLWithString:myURLString];

                NSData* data = [NSData dataWithContentsOfURL:url];

                if ((unsigned long)data.length > 3) {
                    NSArray *ys_avatars = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
                    if(ys_avatars) {
                        for (int j=0;j<ys_avatars.count;j++) {
                            if( ys_avatars[j][@"title"]==[NSNull null] ) {
                                [_Title1 addObject: @""];
                            } else {
                                [_Title1 addObject:ys_avatars[j][@"title"]];
                            }

                            if( ys_avatars[j][@"author"]==[NSNull null] ) {
                                [_Author1 addObject: @""];
                            }

                            [_Author1 addObject: ys_avatars[j][@"author"]];

                            if( ys_avatars[j][@"featured_img"]==[NSNull null] ) {
                                [_Images1 addObject: @""];
                            } else {
                                [_Images1 addObject: ys_avatars[j][@"featured_img"]];

                            }

                            if( ys_avatars[j][@"content"]==[NSNull null] ) {
                                [_Details1 addObject: @""];
                            } else {
                                [_Details1 addObject:ys_avatars[j][@"content"]];
                            }

                            if( ys_avatars[j][@"permalink"]==[NSNull null] ) {
                                [_link1 addObject: @""];
                            } else {
                                [_link1 addObject:ys_avatars[j][@"permalink"]];
                            }

                            if( ys_avatars[j][@"date"]==[NSNull null] ) {
                                [_Date1 addObject: @""];
                            } else {
                                NSString *newStr=[ys_avatars[j][@"date"] substringToIndex:[ys_avatars[j][@"date"] length]-3];
                                [_Date1 addObject:newStr];
                            }
                        }
                    } else {
                        NSLog(@"error");
                    }
                    [self.myTableView reloadData];
                } else {
                    if(IDIOM == IPAD){
                        [self.myTableView reloadData];

                        self.tableView.separatorColor = [UIColor colorWithRed:255/255.0 green:255/255.0 blue:255/255.0 alpha:1.0];

                        label = [[UILabel alloc] initWithFrame:CGRectMake(150, 200, 200, 100)];
                        label.text=@"No Article Found";
                        label.backgroundColor = [UIColor clearColor];
                        [self.view addSubview:label];
                    } else {
                        [self.myTableView reloadData];
                        self.tableView.separatorColor = [UIColor colorWithRed:255/255.0 green:255/255.0 blue:255/255.0 alpha:1.0];
                        label = [[UILabel alloc] initWithFrame:CGRectMake(90, 100, 200, 100)];
                        label.text=@"No Article Found";
                        label.backgroundColor = [UIColor clearColor];
                        [self.view addSubview:label];
                    }
                }
            }
            @catch (NSException *exception) { }
        }
        dispatch_async(dispatch_get_main_queue(), ^{
            [indicator performSelectorOnMainThread:@selector(stopAnimating) withObject:nil waitUntilDone:YES];
        });
    });

    [self.mySearchBar resignFirstResponder];
}

【问题讨论】:

    标签: ios objective-c json uitableview asynchronous


    【解决方案1】:

    您的基本问题是您试图从后台线程更新 UI。 所有 UI 更新必须在主线程/队列上完成。

    通常最简单的方法是使用:

    dispatch_async(dispatch_get_main_queue(), ^{
        // code to run on the main queue
    });
    

    当您在此处停止 UIActiviteIndicatorView 时,我实际上看到您正在使用它:

    dispatch_async(dispatch_get_main_queue(), ^{
        [indicator performSelectorOnMainThread:@selector(stopAnimating) withObject:nil waitUntilDone:YES];
    });
    

    但是,在这种情况下,您实际上将 stopAnimating 方法分派到主队列两次。你只需要这个:

    dispatch_async(dispatch_get_main_queue(), ^{
        [indicator stopAnimating];
    });
    

    至于您的表没有更新,那是因为您需要将所有 reloadData 调用分派到主队列。

    您的代码中有很多地方需要分派回主队列,但与其将dispatch_async 中的所有位置都包装到主队列中,还有一种更简单的方法。我看到的唯一地方是你实际上在做一些应该在后台线程上做的事情是这一行:

    NSData* data = [NSData dataWithContentsOfURL:url];
    

    这意味着您可以在方法开始时去掉dispatch_async(queue, ^{...});,而只在调用[NSData dataWithContentsOfUrl:url] 之前这样做。然后,dispatch_async 之后立即回到主队列。

    像这样:

    -(void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
    {
        [indicator startAnimating];
        indicator.hidesWhenStopped = YES;
    
        NSString *searchText=searchBar.text;
        NSString *trimmedString = [searchText stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceCharacterSet]];
        if (trimmedString.length==0) {
            isFilter=NO;
    
            UIAlertView *noConn = [[UIAlertView alloc] initWithTitle:@"ERROR" message:@"Please enter something in search bar" delegate:self cancelButtonTitle:nil otherButtonTitles:@"ok", nil];
            [noConn show];
        } else {
            NSString *searchNew = [trimmedString stringByReplacingOccurrencesOfString:@" " withString:@"%20"];
            isFilter=YES;
            @try {
                [label removeFromSuperview];
                _Title1 = [[NSMutableArray alloc] init];
                _Author1 = [[NSMutableArray alloc] init];
                _Images1 = [[NSMutableArray alloc] init];
                _Details1 = [[NSMutableArray alloc] init];
                _link1 = [[NSMutableArray alloc] init];
                _Date1 = [[NSMutableArray alloc] init];
                NSString* myURLString = [NSString stringWithFormat:@"www.example.com=%@", searchNew];
                NSURL *url = [NSURL URLWithString:myURLString];
    
                dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                    NSData* data = [NSData dataWithContentsOfURL:url];
    
                    dispatch_async(dispatch_get_main_queue(), ^{
                        if ((unsigned long)data.length > 3) {
                            NSArray *ys_avatars = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
                            if(ys_avatars) {
                                for (int j=0;j<ys_avatars.count;j++) {
                                    if( ys_avatars[j][@"title"]==[NSNull null] ) {
                                        [_Title1 addObject: @""];
                                    } else {
                                        [_Title1 addObject:ys_avatars[j][@"title"]];
                                    }
    
                                    if( ys_avatars[j][@"author"]==[NSNull null] ) {
                                        [_Author1 addObject: @""];
                                    }
    
                                    [_Author1 addObject: ys_avatars[j][@"author"]];
    
                                    if( ys_avatars[j][@"featured_img"]==[NSNull null] ) {
                                        [_Images1 addObject: @""];
                                    } else {
                                        [_Images1 addObject: ys_avatars[j][@"featured_img"]];
    
                                    }
    
                                    if( ys_avatars[j][@"content"]==[NSNull null] ) {
                                        [_Details1 addObject: @""];
                                    } else {
                                        [_Details1 addObject:ys_avatars[j][@"content"]];
                                    }
    
                                    if( ys_avatars[j][@"permalink"]==[NSNull null] ) {
                                        [_link1 addObject: @""];
                                    } else {
                                        [_link1 addObject:ys_avatars[j][@"permalink"]];
                                    }
    
                                    if( ys_avatars[j][@"date"]==[NSNull null] ) {
                                        [_Date1 addObject: @""];
                                    } else {
                                        NSString *newStr=[ys_avatars[j][@"date"] substringToIndex:[ys_avatars[j][@"date"] length]-3];
                                        [_Date1 addObject:newStr];
                                    }
                                }
                            } else {
                                NSLog(@"error");
                            }
                            [self.myTableView reloadData];
                        } else {
                            if(IDIOM == IPAD){
                                [self.myTableView reloadData];
    
                                self.tableView.separatorColor = [UIColor colorWithRed:255/255.0 green:255/255.0 blue:255/255.0 alpha:1.0];
    
                                label = [[UILabel alloc] initWithFrame:CGRectMake(150, 200, 200, 100)];
                                label.text=@"No Article Found";
                                label.backgroundColor = [UIColor clearColor];
                                [self.view addSubview:label];
                            } else {
                                [self.myTableView reloadData];
                                self.tableView.separatorColor = [UIColor colorWithRed:255/255.0 green:255/255.0 blue:255/255.0 alpha:1.0];
                                label = [[UILabel alloc] initWithFrame:CGRectMake(90, 100, 200, 100)];
                                label.text=@"No Article Found";
                                label.backgroundColor = [UIColor clearColor];
                                [self.view addSubview:label];
                            }
                        }
    
                        [indicator stopAnimating];
                    });
                });
            }
            @catch (NSException *exception) { }
        }
    
        [self.mySearchBar resignFirstResponder];
    }
    

    注意:您在这一种方法中做了很多工作。我建议将其拆分为多种方法,以使您的代码更具可读性和可维护性。

    【讨论】:

    【解决方案2】:

    尝试使用NSURLConnection,这样可以省去很多麻烦,并使您的 URL 请求更易于管理

    @interface myTableView : UITableViewController<NSURLConnectionDelegate>{
         NSMutableData *_responseData;
    }
    

    然后使用委托方法解析接收到的数据,停止你的indicatorview,并重新加载你的tableview

    #pragma mark NSURLConnection Delegate Methods
    
    - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
        // A response has been received, this is where we initialize the instance var you created
        // so that we can append data to it in the didReceiveData method
        // Furthermore, this method is called each time there is a redirect so reinitializing it
        // also serves to clear it
        _responseData = [[NSMutableData alloc] init];
    }
    
    - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
        // Append the new data to the instance variable you declared
        [_responseData appendData:data];
    }
    
    - (NSCachedURLResponse *)connection:(NSURLConnection *)connection
                      willCacheResponse:(NSCachedURLResponse*)cachedResponse {
        // Return nil to indicate not necessary to store a cached response for this connection 
        return nil;
    }
    
    - (void)connectionDidFinishLoading:(NSURLConnection *)connection {
        // The request is complete and data has been received
        // You can parse the stuff in your instance variable now
    
    }
    
    - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
        // The request has failed for some reason!
        // Check the error var
    }
    

    并在任何你想要的地方发出你的 URL 请求

    // Create the request.
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://google.com"]];
    
    // Create url connection and fire request
    NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
    

    source

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多