异步UIView内容在返回到主线程时未显示



加载UIViewController时,我基本上在页面中间放置一个微调器,直到加载内容,然后返回主线程添加子视图:

- (void)viewDidLoad {
    [super viewDidLoad];
    UIActivityIndicatorView *aiv_loading = ... // etc
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // Load content
        NSString *s_checkout = [[BRNetwork sharedNetwork] getCheckoutInstructionsForLocation:self.locBooking.location];
        UIView *v_invoice_content = [[BRNetwork sharedNetwork] invoiceViewForBooking:locBooking.objectId];
        // Display the content
        dispatch_sync(dispatch_get_main_queue(), ^{
            if (s_checkout && v_invoice_content) {
                [aiv_loading removeFromSuperview];
                [self showContentWithText:s_checkout AndInvoice:v_invoice_content];
            } else {
                NSLog(@"No data received!"); // is thankfully not called
            }
        });
    });
}
- (void) showContentWithText:(NSString *)s_checkout AndInvoice:(UIView *)v_invoice {
    [self.view addSubview:[self checkoutTextWithText:s_checkout]]; // Most of the time displayed text
    [self.view addSubview:[self completeCheckout]]; // always Displayed UIButton
    [self.view addSubview:[self divider]]; // always displayed UIImageView
    // Summary title
    UILabel *l_summary = [[UILabel alloc] initWithFrame:CGRectMake(0, [self divider].frame.origin.y + 6 + 10, self.view.bounds.size.width, 20)];
    l_summary.text = NSLocalizedString(@"Summary", nil);
    [self.view addSubview:l_summary];
    CGRect totalRect = CGRectMake([self divider].frame.origin.x, [self divider].frame.origin.y + 6 + 30, self.view.bounds.size.width - [self divider].frame.origin.x, 90);
    _v_invoice = v_invoice;
    _v_invoice.frame = totalRect;
    [self.view addSubview:[self v_invoiceWithData:v_invoice]]; // THIS Pretty much never displayed
    UITextView *l_invoice = [[UITextView alloc] initWithFrame:CGRectMake(0, _v_invoice.frame.origin.y + _v_invoice.frame.size.height + offset, 320.0, 50)];
    l_invoice.text = NSLocalizedString(@"summary_emailed", nil);
    [self.view addSubview:l_invoice]; // Always displayed
}

但是,并不是所有内容都显示出来。发票一开始从不在那里,但几分钟后就会显示出来。另一个异步创建的字符串s_content有时不会显示。

这似乎是随机的内容创建。最终结果是相当整洁的,但对于生产版本来说并不可靠。

我使用了未记录的[self.view recursiveDescription]来检查是否所有东西都在那里,即使我看不到,它们也都有似乎正确的框架。

有指针吗?-layout子视图没有帮助!-为发票视图添加背景色就是显示背景色

我怀疑这一行是您的问题:

UIView *v_invoice_content = [[BRNetwork sharedNetwork] invoiceViewForBooking:locBooking.objectId];

当您在后台调度队列中调用它时。任何涉及UIKit的工作都应该在主队列/线程上完成。将其移动到主线程块中,或者如果构建视图依赖于网络调用中的数据,请更改invoiceViewForBooking方法以首先返回数据,然后使用该数据在主线程中构建视图。

   dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // Load content
        NSString *s_checkout = [[BRNetwork sharedNetwork] getCheckoutInstructionsForLocation:self.locBooking.location];
        id someData = [[BRNetwork sharedNetwork] invoiceDataForBooking:locBooking.objectId];
        // Display the content
        dispatch_async(dispatch_get_main_queue(), ^{
            UIView *v_invoice_content = [invoiceViewWithData:someData];
        });
    });

我还建议在主队列中使用dispatch_async而不是dispatch_sync

已解决!我最终删除了dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)只将异步块留在主队列上:

// Display the content from a new async block on the main queue.
dispatch_async(dispatch_get_main_queue(), ^{
    [aiv_loading removeFromSuperview];
    NSString *s_checkout = [[BRNetwork sharedNetwork] getCheckoutInstructionsForLocation:self.locBooking.location];
    [self showContentWithText:s_checkout AndInvoice:[[BRNetwork sharedNetwork] invoiceViewForBooking:locBooking.objectId]];
});

它做到了,viewcontroller的视图可以在不等待视图加载远程数据的情况下出现!

您应该将代码放在viewDidAppear而不是viewDidLoad中。与显示相关的内容(甚至启动异步块)应该始终在viewWillAppearviewDidAppear中。

此外,我建议您使用dispatch_async(dispatch_get_main_queue(), ^{})而不是dispatch_sync,因为您仍然希望您的块异步执行(但在主线程上)。

不要忘记在viewDidAppear中调用超级方法。

最新更新