斯坦福iOS7 2013-2014秋Assignment 6的一種答案 #9

這篇文章是針對斯坦福iOS7 2013-1014的公開課Assignment 6 Top Regions所進行的解答的第九部分。

8. Display a thumbnail image of a photo in any table view row that shows Flickr photo information. You must download these on demand only (i.e. do not ask for a thumbnail until the user tries to display that photo in a row). Once a thumbnail has been downloaded, you must store the thumbnail’s data in Core Data (i.e. don’t ask Flickr for it again). Don’t forget that table view cells are reused!  

本節主要是完成thumbnail的顯示和Extra Task 1。

在RegionPhotosTVC中,增加下面的代碼:

- (UITableViewCell *)tableView:(UITableView *)tableView
         cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    ...
    cell.imageView.image = [UIImage imageWithData:photo.thumbnail];
    if (!cell.imageView.image) {
        dispatch_queue_t q = dispatch_queue_create("Thumbnail Flickr Photo", 0);
        dispatch_async(q, ^{
            NSData *imageData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:photo.thumbnailURL]];
            [photo.managedObjectContext performBlock:^{
                photo.thumbnail = imageData;
                dispatch_async(dispatch_get_main_queue(), ^{
                    [cell setNeedsLayout];
                });
            }];
        });
    }
    ...
}
運行一下,會看到下面的結果:


Extra  Credit 1. Still allow the user to force a fetch using the refreshControl in the appropriate table views. And use the cellular network to fetch only when the user prompts you via a refreshControl in this manner (otherwise restrict all your URLforRecentGeoreferencedPhotos fetches to WiFi only). If you are a bit stumped about how to know when to endRefreshing in a table view, think about whether there are some NSNotifications flying around that you could listen in on.

首先在RegionsTVC的storyboard中開啓refeshControl,並把它連接到代碼裏,命名爲fetchRegions。由於下載是通過AppDelegate來完成的,所以我們需要一個橋樑——通知,來實現兩個類之間的交流。當下拉時,RegionsTVC發送個通知,然後AppDelegate接收它並開始下載;下載完畢後,AppDelegate發佈個通知說我下載完了,然後RegionsTVC接收這個通知並讓refreshControl endRefreshing。

首先在PhotoDatabaseAvailability.h中添加:

<span style="font-size:18px;">#define STARTCELLULARFLICKRFETCHNOTIFICATION @"startCellularFlickrFetch"
#define FINISHEDCELLULARFLICKRFETCHNOTIFICATION @"finishedC</span>
然後實現fetchRegions
<span style="font-size:18px;">- (IBAction)fetchRegions
{
    [[NSNotificationCenter defaultCenter] postNotificationName:STARTCELLULARFLICKRFETCHNOTIFICATION object:self];
}</span>
接收下載完成的通知
<span style="font-size:18px;">- (void)viewDidLoad
{
    [super viewDidLoad];
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(finishedRefreshing)
                                                 name:FINISHEDCELLULARFLICKRFETCHNOTIFICATION
                                               object:nil];
}</span>
<span style="font-size:18px;">- (void)finishedRefreshing
{
    if ([FlickrHelper isCellularDownloadSession]) {
        [self.refreshControl endRefreshing];
    }
}</span>

這裏需要在FlickrHelper中提供一個public方法:

+ (BOOL)isCellularDownloadSession
{
    FlickrHelper *fh = [FlickrHelper sharedFlickrHelper];
    return fh.currentDownloadSession.configuration.allowsCellularAccess;
}
程序啓動時接收開始下載的通知:

<span style="font-size:18px;">- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
            ...
            [[NSNotificationCenter defaultCenter] addObserver:self
                                                     selector:@selector(startCellularFlickrFetch)
                                                         name:STARTCELLULARFLICKRFETCHNOTIFICATION
                                                       object:nil];
        ...
}</span>
創建個方法來確定是否使用cellular下載,並修改startFlickrFetch方法

<span style="font-size:18px;">- (void)startFlickrFetchAllowingCellularAccess:(BOOL)cellular
{
    [FlickrHelper startBackgroundDownloadRecentPhotosOnCompletion:^(NSArray *photos, void (^whenDone)()) {
        [self useDocumentWithFlickrPhotos:photos];
        if (whenDone) whenDone();
    } allowingCellularAccess:cellular];
}
 
- (void)startCellularFlickrFetch
{
    [self startFlickrFetchAllowingCellularAccess:YES];
}
 
- (void)startFlickrFetch
{
    [self startFlickrFetchAllowingCellularAccess:NO];
}</span>
當然,我們也必須修改Flickr Helper的public方法,這樣才能處理是否使用cellular
+ (void)startBackgroundDownloadRecentPhotosOnCompletion:(void (^)(NSArray *photos, void(^whenDone)()))completionHandler
                                 allowingCellularAccess:(BOOL)cellular;
在後臺下載中我們已經禁用了cellular下載,然而我們需要在一般的下載中把默認的下載禁用cellular
- (NSURLSession *)downloadSession
{
        ...
            NSURLSessionConfiguration *config = [NSURLSessionConfiguration backgroundSessionConfiguration:FLICKR_FETCH];
            config.allowsCellularAccess = NO;
            _downloadSession = [NSURLSession sessionWithConfiguration:config
                                                             delegate:self
                                                        delegateQueue:nil];
        ...
}
可惜session一旦建立之後,我們不能改變它的配置,也就是不能直接通過改變config.allowsCellularAccess來允許蜂窩數據(cellular)下載。所以,我們創建3個屬性來啓用和禁用cellular。

@property (strong, nonatomic) NSURLSession *cellularDownloadSession;
@property (strong, nonatomic) NSURLSession *currentDownloadSession;
@property (nonatomic) BOOL allowingCellularAccess;
cellular的下載session和普通的downloadSession是一樣的,只是允許啓用蜂窩數據

#define FLICKR_FETCH_CELLULAR @"Cellular Flickr Download Session"
- (NSURLSession *)cellularDownloadSession
{
    if (!_cellularDownloadSession) {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            NSURLSessionConfiguration *config = [NSURLSessionConfiguration backgroundSessionConfiguration:FLICKR_FETCH_CELLULAR];
            config.allowsCellularAccess = YES;
            _cellularDownloadSession = [NSURLSession sessionWithConfiguration:config
                                                             delegate:self
                                                        delegateQueue:nil];
        });
    }
    return _cellularDownloadSession;
}
確定當前的session是否允許cellular

- (NSURLSession *)currentDownloadSession
{
    if (self.allowingCellularAccess) return self.cellularDownloadSession;
    return self.downloadSession;
}
在類方法中,設置是否允許cellular和使用當前session

+ (void)startBackgroundDownloadRecentPhotosOnCompletion:(void (^)(NSArray *photos, void(^whenDone)()))completionHandler allowingCellularAccess:(BOOL)cellular
{
    FlickrHelper *fh = [FlickrHelper sharedFlickrHelper];
    fh.allowingCellularAccess = cellular;
    NSURLSession *session = fh.currentDownloadSession;
    [session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
        ...
    }];
}
同時修改另一個類方法,由於現在有兩個session,所以我們需要修改task的標識符,讓它包含session的描述:

+ (void)startBackgroundDownloadRegionForPlaceID:(NSString *)placeID onCompletion:(RegionCompletionHandler)completionHandler
{
    FlickrHelper *fh = [FlickrHelper sharedFlickrHelper];
    NSURLSession *session = fh.currentDownloadSession;
    [session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
        NSURLSessionDownloadTask *task = [session downloadTaskWithURL:[FlickrFetcher URLforInformationAboutPlace:placeID]];
        task.taskDescription = FLICKR_FETCH_REGION;
        [fh.regionCompletionHandlers setObject:[completionHandler copy]
                                        forKey:[NSString stringWithFormat:@"%@%d", session.description, task.taskIdentifier]];
        [task resume];
    }];
}

同時,我們需要修改下載完成時調用的key

- (void)URLSession:(NSURLSession *)session
      downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
        ...
        RegionCompletionHandler regionCompletionHandler = [self.regionCompletionHandlers[[NSString stringWithFormat:@"%@%d", session.description, downloadTask.taskIdentifier]] copy];
        ...
}
我們再確保後臺下載不允許蜂窩數據

+ (void)loadRecentPhotosOnCompletion:(void (^)(NSArray *places, NSError *error))completionHandler
{
    ...
    [FlickrHelper sharedFlickrHelper].allowingCellularAccess = NO;
    ...
}
最後,使用當前session調用completionHandler

- (void)downloadTasksMightBeComplete
{
    ...
        NSURLSession *session = self.currentDownloadSession;
        [session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
            ...
        }];
    }
}
那麼什麼時候通知下載完成了呢?有兩處,一處是沒有新的regions,第二處是新region的文件保存的時候

+ (void)loadRegionNamesFromFlickrIntoManagedObjectContext:(NSManagedObjectContext *)context
{
    ...
    if (!matches || ![matches count]) {
        [[NSNotificationCenter defaultCenter] postNotificationName:FINISHEDCELLULARFLICKRFETCHNOTIFICATION
                                                            object:self];
    } else {
                                ...
                                if (saveDocument) {
                                    ...
                                    [[NSNotificationCenter defaultCenter] postNotificationName:FINISHEDCELLULARFLICKRFETCHNOTIFICATION
                                                                                        object:self];
                                }
        ...
    }
}

好了,在蜂窩數據模式下,運行一下。










發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章