|
101 | 101 | [self.view addSubView:self.pageViewController.view]; |
102 | 102 | } |
103 | 103 | ``` |
104 | | -我要指出的是,简便起见,在我的应用里我禁用了横向展示,因为这不是一本关于`autoresizingMask`或者`autoLayout`的书。你可以通过[Eria Sadun的书]()了解更多关于`autoLayout`方面的细节。 |
| 104 | +我要指出的是,简便起见,在我的应用里我禁用了横向展示,因为这不是一本关于`autoresizingMask`或者`autoLayout`的书。你可以通过[Eria Sadun的书](http://www.amazon.com/Layout-Demystified-Edition-Mobile-Programming/dp/0321967194)了解更多关于`autoLayout`方面的细节。 |
| 105 | + |
| 106 | +下面我们来了解一下UIPageViewController的数据源协议和代理协议。 |
| 107 | + |
| 108 | +``` |
| 109 | +- (void)pageViewController:(UIPageViewController *)pageViewController |
| 110 | + didFinishAnimating: (BOOL)finished |
| 111 | + previousViewControllers:(NSArray *)previousViewControllers |
| 112 | + transitionCompleted:(BOOL)completed{ |
| 113 | + self.title = [[self.pageViewController.viewControllers.firstObject photoModel] photoName]; |
| 114 | + [self.delegate userDidScroll:self toPhotoAtIndex:[self.pageViewController.viewControllers.firstObject photoIndex]]; |
| 115 | + } |
| 116 | + |
| 117 | +- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(FRPPhotoViewController *)viewController{ |
| 118 | + return [self photoViewControllerForIndex:viewController.photoIndex - 1]; |
| 119 | +} |
| 120 | +
|
| 121 | +- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(FRPPhotoViewController *)viewController { |
| 122 | + return [self photoViewControllerForIndex:viewController.photoIndex + 1]; |
| 123 | +} |
| 124 | +
|
| 125 | +``` |
| 126 | +虽然这些方法没有技术上的`reactive`,却体现出一定意义上的实用性。我很佩服这种在特殊类型的视图控制器上的抽像,干得漂亮,Apple! |
| 127 | + |
| 128 | +我们的视图控制器创建方法,类似下面这样: |
| 129 | + |
| 130 | +``` |
| 131 | +- (FRPPhotoViewController *)photoViewControllerForIndex:(NSInteger)index{ |
| 132 | + if (index >= 0 && index < self.photoModelArray.count){ |
| 133 | + FRPPhotoModel *photoModel = self.photoModelArray[index]; |
| 134 | + |
| 135 | + FRPPhotoViewController *photoViewController = [[FRPPhotoViewController alloc] initWithPhotoModel:photoModel index:index]; |
| 136 | + |
| 137 | + return photoViewController; |
| 138 | + } |
| 139 | + |
| 140 | + //Index was out of bounds, return nil |
| 141 | + return nil; |
| 142 | + } |
| 143 | +``` |
| 144 | +它基本上创建比配置了一个我们将要使用的UIViewController的子视图控制器FRPPhotoViewController。下面是他的头文件: |
| 145 | + |
| 146 | +``` |
| 147 | +@class FRPPhotoModel; |
| 148 | +
|
| 149 | +@interface FRPPhotoViewController : UIViewController |
| 150 | +- (instancetype)initWithPhotoModel:(FRPPhotoModel *)photoModel index:(NSInteger)photoIndex; |
| 151 | +
|
| 152 | +@property (nonatomic, readonly) NSInteger photoIndex; |
| 153 | +@property (nonatomic, readonly) FRPPhotoModel * photoModel; |
| 154 | + |
| 155 | +@end |
| 156 | +
|
| 157 | +``` |
| 158 | +这个视图控制器非常简单:显示一个photoModel下的高清图片,并提示photoImporter(单例对象)下载这个图片。它是如此简单,我现在就告诉你它的全部实现。 |
| 159 | + |
| 160 | +``` |
| 161 | +//Model |
| 162 | +#import "FRPPhotoModel.h" |
| 163 | +
|
| 164 | +//Utilities |
| 165 | +#import "FRPPhotoImporter.h" |
| 166 | +#import <SVProgressHUD.h> |
| 167 | +
|
| 168 | +@interface FRPPhotoViewController () |
| 169 | +//Private assignment |
| 170 | +@property (nonatomic, assign) NSInteger photoIndex; |
| 171 | +@property (nonatomic, strong) FRPPhotoModel *photoModel; |
| 172 | +
|
| 173 | +//Private properties |
| 174 | +@property (nonatomic, weak) UIImageView * imageView; |
| 175 | +
|
| 176 | +@end |
| 177 | +
|
| 178 | +@implementation FRPPhotoViewController |
| 179 | +
|
| 180 | +- (instancetype)initWithPhotoModel:(FRPPhotoModel *)photoModel index:(NSInteger)photoIndex{ |
| 181 | + self = [self init]; |
| 182 | + if (!self) return nil; |
| 183 | + |
| 184 | + self.photoModel = photoModel; |
| 185 | + self.photoIndex = photoIndex; |
| 186 | + |
| 187 | + return self; |
| 188 | +} |
| 189 | +
|
| 190 | +- (void)viewDidLoad{ |
| 191 | + [super viewDidLoad]; |
| 192 | + |
| 193 | + //Configure self's view |
| 194 | + self.view.backGroundColor = [UIColor blackColor]; |
| 195 | + |
| 196 | + //Configure subViews |
| 197 | + UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.view.bounds]; |
| 198 | + |
| 199 | + RAC(imageView, image) = [RACObserve(self.photoModel, fullsizeData) map:^id (id value){ |
| 200 | + return [UIImage imageWithData:value]; |
| 201 | + }]; |
| 202 | + |
| 203 | + imageView.contentMode = UIViewContentModeScaleAspectFit; |
| 204 | + [self.view addSubView:imageView]; |
| 205 | + self.imageView = imageView; |
| 206 | +} |
| 207 | +
|
| 208 | +- (void)viewWillAppear:(BOOL)animated{ |
| 209 | + [super viewWillAppear:animated]; |
| 210 | + [SVProgressHUD show]; |
| 211 | + |
| 212 | + //Fetch data |
| 213 | + [[FRPPhotoImporter fetchPhotoDetails:self.photoModel] |
| 214 | + subscribeError:^(NSError *error){ |
| 215 | + [SVProgressHUD showErrorWithStatus:@"Error"]; |
| 216 | + } |
| 217 | + completed:^{ |
| 218 | + [SVProgressHUD dismiss]; |
| 219 | + }]; |
| 220 | +} |
| 221 | +
|
| 222 | +@end |
| 223 | +
|
| 224 | +``` |
| 225 | +就像我们的collectionViewCell中那样,我们将UIImageView的image属性和数据模型的某个属性映射后的值绑定,所不同的是ViewController不需要考虑复用,所以我们不必计较怎么取消imageView的订阅---当imageView对象解除分配的时候,订阅将会被取消。 |
| 226 | + |
| 227 | +这个实现里面另一个有趣的部分在`viewWillAppear:`里: |
| 228 | + |
| 229 | +``` |
| 230 | +[SVProgressHUD show]; |
| 231 | +//Fetch data |
| 232 | +[[FRPPhotoImporter fetchPhotoDetails:self.photoModel] |
| 233 | + subscribeError:^(NSError * error){ |
| 234 | + [SVProgressHUD showErrorWithStatus:@"Error"]; |
| 235 | + } |
| 236 | + completed:^{ |
| 237 | + [SVProgressHUD dismiss]; |
| 238 | + }]; |
| 239 | +``` |
| 240 | +没有收到错误或者完成信息之前,我们必须给用户展示网络请求的状态。你看,500px的受欢迎的照片的API接口只返回了一个照片的大概信息,但我们需要这个照片更详细的信息,所以我们必须调用第二个API接口来获取每一个照片的详细信息(包括全尺寸照片的URL)。 |
| 241 | + |
| 242 | +``` |
| 243 | ++ (NSURLRequest *)photoURLRequest:(FRPPhotoModel *)photoModel{ |
| 244 | + return [AppDelegate.apiHelper urlRequestForPhotoID:photoModel.identifier.integerValue]; |
| 245 | +} |
| 246 | +``` |
| 247 | + |
| 248 | +我们还没有实现`fetchPhotoDetails:`方法,所以现在我们回到`FRPPhotoImporter`中,在头文件中定义这个方法,在实现文件中实现它。 |
| 249 | + |
| 250 | +``` |
| 251 | ++ (RACReplaySubject *)fetchPhotoDetails:(FRPPhotoModel *)photoModel { |
| 252 | + RACReplaySubject * subject = [RACReplaySubject subject]; |
| 253 | + NSURLRequest *request = [self photoURLRequest:photoModel]; |
| 254 | + |
| 255 | + [NSURLConnection sendAsynchronousRequest:request |
| 256 | + queue:[NSOperationQueue mainQueue] |
| 257 | + completionHandler:^ (NSURLResponse *response, NSData * data, NSError *connectionError){ |
| 258 | + if(data){ |
| 259 | + id results = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil][ @"photo" ]; |
| 260 | + |
| 261 | + [self configurePhotoModel:photoModel withDictionary:results]; |
| 262 | + [self downloadFullsizedImageForPhotoModel:photoModel]; |
| 263 | + |
| 264 | + [subject sendNext:photoModel]; |
| 265 | + [subject sendCompleted]; |
| 266 | + } |
| 267 | + else{ |
| 268 | + [subject sendError:connectionError]; |
| 269 | + } |
| 270 | + }]; |
| 271 | + |
| 272 | + return subject; |
| 273 | +} |
| 274 | +``` |
| 275 | + |
| 276 | +这种方法跟前面我们看到的`importPhotos`方法模式一样,我们的`downloadFullsizedImageForPhotoModel:`方法跟`downloadThumbnailForPhotoModel:`方法也是一样的。除了这两者之外,还有什么重要的抽象方法呢?让我们来完成我们的缩略图方法。 |
| 277 | + |
| 278 | +``` |
| 279 | +--待续 page56 |
| 280 | +``` |
| 281 | + |
0 commit comments