如何让 UIPageViewController 知道我的异步下载何时完成?

Posted

技术标签:

【中文标题】如何让 UIPageViewController 知道我的异步下载何时完成?【英文标题】:how can I let UIPageViewController know when my async download is complete? 【发布时间】:2013-12-22 21:28:48 【问题描述】:

我目前在我的项目中设置了一个 UIPageViewController,几乎就像默认的基于页面的应用程序模板一样。

但是,在我的 ModelController 的 init 方法中,我使用 NSURLConnection 将数据异步下载到应该显示在 PageViewController 上的数组(图像)中。

这意味着当我的根视图控制器启动并初始化一个起始视图控制器时,资源可能尚未下载,然后模型控制器正在从一个空数组中获取内容,这会导致应用程序崩溃。

如何实现在 PageView 中显示图像的安全方式?我正在考虑使用带有活动指示器的空视图控制器作为起始视图控制器,但我不知道如何让模型控制器知道下载何时完成,以便我可以使用图像更新视图。

我的根视图控制器(这是 uipageviewcontroller 委托)

@interface CSAPromoViewController ()
@property (readonly, strong, nonatomic) CSAPromoModelController *modelController;
@end

@implementation CSAPromoViewController

@synthesize modelController = _modelController;

- (void)viewDidLoad

    [super viewDidLoad];

    self.pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];
    self.pageViewController.delegate = self;

    CSAPageDataViewController *startingViewController = [self.modelController viewControllerAtIndex:0 storyboard:self.storyboard];
    NSArray *viewControllers = @[startingViewController];
    [self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];

    self.pageViewController.dataSource = self.modelController;

    [self addChildViewController:self.pageViewController];
    [self.view addSubview:self.pageViewController.view];

    //set page view controller's bounds
    CGRect pageViewRect = self.view.bounds;
    self.pageViewController.view.frame = pageViewRect;

    [self.pageViewController didMoveToParentViewController:self];

    self.view.gestureRecognizers = self.pageViewController.gestureRecognizers;


我的模型控制器(这是数据源)

@interface CSAPromoModelController()
@property (readonly, strong, nonatomic) NSArray *promosArray;
@end

@implementation CSAPromoModelController
-(id)init

    self = [super init];
    if (self) 
        NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://blah.com"]
                                             cachePolicy:NSURLRequestUseProtocolCachePolicy
                                         timeoutInterval:60.0];
        [NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) 
            _promosArray = [self parseJSON:data];
        ];
    
    return self;


- (CSAPageDataViewController *)viewControllerAtIndex:(NSUInteger)index storyboard:(UIStoryboard *)storyboard

    // Return the data view controller for the given index.
    if (([self.promosArray count] == 0) || (index >= [self.promosArray count] / 2)) 
        return nil;
    

    // Create a new view controller and pass suitable data.
    CSAPageDataViewController *dataViewController = [storyboard instantiateViewControllerWithIdentifier:@"CSAPageDataViewController"];
    dataViewController.promoOne = [self.promosArray objectAtIndex:index * 2];
    dataViewController.promoTwo = [self.promosArray objectAtIndex:(index * 2) + 1];
    return dataViewController;

数据视图控制器

@implementation CSAPageDataViewController

- (void)viewDidLoad

    [super viewDidLoad];
// Do any additional setup after loading the view.
    self.promoLabelTop.text = [self.promoOne name];
    self.promoImageTop.image = [self.promoOne image];
    self.promoLabelBottom.text = [self.promoTwo name];
    self.promoImageBottom.image = [self.promoTwo image];

【问题讨论】:

你能显示一些来自页面控制器的代码吗? 【参考方案1】:

您要解决的问题是异步。但是,您的方法是解决 同步 问题。

例如,您的类CSAPromoModelController 本质上是异步的。这是因为它的 init 方法调用了一个异步方法,因此您的类被异步“感染”了。

您可能会考虑重新设计,其中类 CSAPromoModelController 成为具有完整处理程序的 NSOperation 的子类,例如CSAPromoModelOperation最终结果是图像数组。 imageArray 成为您的 CSAPromoViewController 的一个 ivar。 CSAPromoViewController 将具有创建 CSAPromoModelController 对象的方法,该对象将使用图像进行初始化。操作的完成处理程序传递图像数组。在完成处理程序中,您基本上执行与原始 viewDidLoad 方法中相同的语句以设置控制器。

您将使用如下操作:

- (void)viewDidLoad

    [super viewDidLoad];

    self.pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];
    self.pageViewController.delegate = self;

    NSURLRequest *request = ...
    CSAPromoModelOperation* op = 
      [CSAPromoModelOperation alloc] initWithRequest:request
                                     completion:^(NSArray* result, NSError*error) 
    
        // assuming we are executing on the main thread!
        if (error == nil) 
            self.imageArray = result;
            CSAPageDataViewController* startingViewController = 
                 [self viewControllerWithImage:self.imageArray[0] 
                                    storyboard:self.storyboard];
            NSArray* viewControllers = @[startingViewController];
            [self.pageViewController setViewControllers:viewControllers
                                              direction:UIPageViewControllerNavigationDirectionForward 
                                               animated:NO 
                                             completion:nil];
            ...
        
        else 
            // handle error
        
    ];

    [op start];

【讨论】:

以上是关于如何让 UIPageViewController 知道我的异步下载何时完成?的主要内容,如果未能解决你的问题,请参考以下文章

旋转到横向时如何让 UIPageViewController 工作?

如何让 UIPageViewController 使用过渡样式滚动?

如何让 UIPageViewController 知道我的异步下载何时完成?

如何设置 UIPageViewController(分页类型)手势委托

如何使 UIPageViewController 像 tableview 重用单元一样重用控制器?

如何在UIPageViewController顶部添加按钮?