【问题标题】:iPhone - Make UITableViewCells appear on view load before NSURLConnection completesiPhone - 在 NSURLConnection 完成之前使 UITableViewCells 出现在视图加载中
【发布时间】:2013-01-17 02:44:43
【问题描述】:

我正在尝试使用情节提要和 JSON 创建一个松散版本的 LazyTabelImages。在我的主 TableViewController 上的 ViewDidLoad 中,我启动了一个 NSURLConnection 来获取 JSON 数据,但是直到连接完成后我的单元格才会加载。我想要与 LazyTableImages 相同的行为,其中单元格加载为空白,然后填充信息(重新加载表数据)。如果我不使用故事板,我可以复制它,因为 LazyTables 不使用故事板,但这不是一个选项。

我已经查看了 LazyTableImages 以尝试找到解决方案,但情节提要有很大的不同(无论如何对我来说)。

有没有一种简单的方法可以让单元格加载为空白?例如,如果设备没有互联网,我仍然希望我的 TableView 显示出来,我会在单元格中放置一条自定义消息。

代码:

我在 viewDidLoad 中初始化连接的部分......

NSURLRequest *urlrequest = [NSURLRequest requestWithURL:[NSURL URLWithString:serverURL]];
    self.dataConnection = [[NSURLConnection alloc] initWithRequest:urlrequest delegate:self];

    [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;

connectionDidFinnishLoading...

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    //ListData below is an array that my data received (JSON) is loaded into. It is then passed to getTableData.
    self.dataConnection = nil;
    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [self performSelectorOnMainThread:@selector(getTableData:) withObject:ListData waitUntilDone:YES];
    });
}

getTableData...

-(void)getTableData:(NSData *)jsonData
{
    NSError *error = nil;
    arrayEntries = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableLeaves error:&error];
    for (int x = 0; x < arrayEntries.count; x++)
    {
        NSMutableDictionary *dic = [arrayEntries objectAtIndex:x];

        //ARecord is a class just like in LazyTableImages that creates objects to keep the icons/data together. The ARecords are loaded into the TableView
        ARecord *arecord = [[ARecord alloc] init];

        NSString *title = [dic objectForKey:@"title"];
        NSString *subt = [dic objectForKey:@"subtitle"];
        NSString *url = [dic objectForKey:@"image_URL"];
        arecord.Icon = nil;
        arecord.URL = url;
        arecord.Name = title;
        arecord.title = subt;

        //this is where I load an array full of the arecord objects.
        [array addObject:arecord];
    }
    [self.tableView reloadData];
}

【问题讨论】:

  • 你在主线程调用网络了吗?如果是这样,网络阻塞 UI 线程。
  • 我曾尝试使用 GCD 将其移出主线程,但随后我只得到一个白屏,因为表永远不会加载。 NURLConnection 中有一个 GCD 调用,用于处理不同线程上的 JSON 解析。
  • 请注意,您的 NSURLConnection (应该)已经是异步的。将其移至后台线程可能是个坏主意。正如 Quinn 所说,在涉及网络的地方“Threads Are Evil™”。
  • 我也记得,但不管我怎么剪;要么将 NSURLConnection 移至后台线程,要么将解析移至后台线程,在 NSURLConnection 完成之前不会加载单元格。我需要将初始下载移出 UI。
  • 你已经使用了asyn newworking,所以不需要使用gcd。

标签: iphone uitableview nsurlconnection


【解决方案1】:

我做过类似的事情。在 viewDidLoad 中:我将表数据的数组设置为 [NSNull null] 的几个对象,但我想在数据下载时显示许多空白行。在 cellForRowAtIndexPath: 我检查 [self.arrayOfTableData objectAtIndex:indexPath.row] = [NSNull null]。如果是这样,则返回一个“空白”单元格,否则使用 ARRecrod 数据加载该单元格。 然后,当 URL 完成时,将 NSNulls 数组替换为您的 ARRecords 数组。

【讨论】:

  • 工作完美,正是我想要的。谢谢!
【解决方案2】:

我用两个对象来做这个。首先,我有一个图像获取器类,它异步下载数据并在完成时通知委托。然后我有一个实现 fetcher 的委托方法的图像视图类。所以像:

@implementation AsyncImageFetcher
-(id)initWithURL:(NSURL *)aURL andDelegate:(id<SomeProtocol>)aDelegate{

  //...

  NSURLRequest *req = [NSURLRequest requestWithURL:aURL];
  //Note that NSURLConnection retains its delegate until the connection
  //terminates! See comments in MyImageView below.
  [NSURLConnection connectionWithRequest:req delegate:self];

  //...
}

//Implement standard connection delegates here. The important part is:
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{

  // ...

  UIImage *anImage = [self decodeDownloadedDataIntoAnImage];
  if([[self delegate] respondsToSelector:@selector(imageFetcher:didFetchImage:)]){
    [[self delegate] imageFetcher:self didFetchImage:anImage];
  }

  //...
}

@end

然后我继承 UIImageViewUIView 或其他东西(取决于你需要有多灵活)来实现委托协议并触发 fetcher:

@implementation MyImageView

-(id)initWithURL:(NSURL *)aURL andPlaceHolderImage:(UIImage *)aPlaceHolder{

  //...

  [self setImage:aPlaceHolder];

  //Note we don't assign this to an ivar or retain it or anything. 
  //That's ok because the NSURLConnection inside the fetcher actually 
  //retains the fetcher itself. So it will live until the connection 
  //terminates -- which is exactly what we want. Thus we disable 
  //CLANG's noisy warnings.
  #pragma clang diagnostic push
  #pragma clang diagnostic ignored "-Wunused-value"
  [[AsyncImageFetcher alloc] initWithURL:aURL andDelegate:self];
  #pragma clang diagnostic pop

  return self;
}


-(void)imageFetcher:(MCMAsyncImageFetcher *)anImageFetcher didFetchImage:(UIImage *)anImage{
  [self setImage:anImage];
}

@end

在您的具体情况下,您只需在-tableView:cellForRowAtIndexPath: 中将MyImageView 设置为单元格的imageView,当然,为其占位符和URL 传递合理的值。

【讨论】:

  • 感谢您的回答。我添加了代码来帮助消除任何混淆。只花了一段时间才得到我需要的内容并正确编辑。
  • 我已经厌倦了这种方式,但我现在的方式是处理我最初的 JSON 调用的最佳方式。我在第二个类中获取图像,这只是为了获取包含 URLS 的 JSON 的第一部分,以便稍后异步获取图像。还有其他想法吗?谢谢
  • 异步加载JSON也是一样的。只是,您可以将下载的 JSON 数据解析为对象,而不是 -decodingDowloadedDataIntoAnImage。而不是使用图像作为委托,您将(可能)使用表的控制器,以便它可以使用可用的新 JSON 数据重新加载表。
【解决方案3】:

由于我没有看到你的代码,我只是在这里给出我的建议:

- (void)viewDidLoad
{
    [super viewDidLoad];

    dispatch_queue_t queue = dispatch_queue_create(NULL, NULL);
    dispatch_async(queue, ^{

        //add your connection code here
        //parse the json and store the data
        //

        dispatch_async(dispatch_get_main_queue(), ^{

            //here to reload your table view again, 
            //since UI related method should run on main thread.

            [YOUR_TABLEVIEW reloadData];

        });

    });

    [YOUR_TABLEVIEW reloadData];
}

注意:确保故事板中的 tableview 已与代码中的 tableview 连接!希望对您有所帮助!

【讨论】:

  • 谢谢。我在想这是我一直需要的方法,但我仍然无法让它发挥作用。我添加了代码来帮助消除混淆。
  • 所以我已经尝试过这种方式,但我只是在调度呼叫中挂断了电话。现在我已经发布了一些代码,还有其他建议吗?谢谢
  • 小心!在线程上建立网络几乎总是一个错误。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-10
相关资源
最近更新 更多