Skip to content

Commit 165520d

Browse files
committed
修改内容
1 parent 5f69470 commit 165520d

File tree

3 files changed

+211
-34
lines changed

3 files changed

+211
-34
lines changed

.gitignore

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Node rules:
22
## Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
33
.grunt
4-
.DS_Store
4+
55
## Dependency directory
66
## Commenting this out is preferred by some people, see
77
## https://docs.npmjs.com/misc/faq#should-i-check-my-node_modules-folder-into-git
@@ -14,3 +14,51 @@ _book
1414
*.epub
1515
*.mobi
1616
*.pdf
17+
18+
# Mac OS X
19+
.DS_Store
20+
.VolumeIcon.icns
21+
.fseventsd
22+
Icon
23+
24+
25+
# Xcode
26+
build/*
27+
*.pbxuser
28+
!default.pbxuser
29+
*.mode1v3
30+
!default.mode1v3
31+
*.mode2v3
32+
!default.mode2v3
33+
*.perspectivev3
34+
!default.perspectivev3
35+
*.xcworkspace
36+
!default.xcworkspace
37+
!xcshareddata
38+
xcuserdata
39+
profile
40+
*.moved-aside
41+
42+
# Build products
43+
build/
44+
*.o
45+
46+
# No other repo directories
47+
.svn
48+
.hg
49+
CVS
50+
51+
# No Sparkle private keys
52+
dsa_priv.pem
53+
54+
# Python
55+
*.pyc
56+
57+
# No Backup Files
58+
*~.nib
59+
*.swp
60+
*~
61+
*(Autosaved).rtfd/
62+
Backup[ ]of[ ]*.pages/
63+
Backup[ ]of[ ]*.key/i
64+
Backup[ ]of[ ]*.numbers/
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,104 @@
11
# 添加FunctionalReactivePixels
2+
一个简单的画廊弄好了,但是我们是不是想看一下高清图呢?当用户点击画廊中的某一个单元格时,我们创建一个新的视图控制器并将其推入到导航堆栈中。
3+
4+
```
5+
- (void)collectionView:(UICollectionView *)collectionView
6+
didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
7+
FRPFullSizePhotoViewController * viewController = [[FRPFullSizePhotoViewController alloc] initWithPhotoModels:self.photosArray currentPhotoIndex:indexPath.item];
8+
9+
viewController.delegate = self;
10+
[self.navigationController pushViewController:viewController animated:YES];
11+
12+
}
13+
```
14+
15+
这个方法没有任何特殊的,只是些一般的OC方法。当然别忘了在当前实现文件里加载视图控制器(FRPFullSizePhotoViewControler)的头文件.现在让我们来创建这个视图控制器(FRPFullSizePhotoViewControler).
16+
17+
创建一个UIViewController的子类FRPFullSizePhotoViewControler,这不会是一个特别的‘Reactive’的视图控制器,实际上大部分只是`UIPageViewController`子视图控制器的模版。
18+
19+
```
20+
@class FRPFullSizePhotoViewController;
21+
22+
@protocol FRPFullSizePhotoViewControllerDelegate <NSOject>
23+
- (void)userDidScroll:(FRPFullSizePhotoViewController *)viewController toPhotoAtIndex:(NSInteger)index;
24+
25+
@end
26+
27+
@interface FRPFullSizePhotoViewController : UIViewController
28+
29+
- (instancetype)initWithPhotoModels:(NSArray *)photoModelArray currentPhotoIndex:(NSInteger)photoIndex;
30+
31+
@property (nonatomic , readonly) NSArray *photoModelArray;
32+
@property (nonatomic, weak) id<FRPFullSizePhotoViewControllerDelegate> delegate;
33+
34+
@end
35+
36+
```
37+
38+
回到画廊视图控制器实现必要的代理方法:
39+
40+
```
41+
- (void)userDidScroll:(FRPFullSizePhotoViewController *)viewController toPhotoAtIndex:(NSInteger)index{
42+
[self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:index inSection:0]
43+
atScrollPosition:UICollectionViewScrollPositionCenteredVertically
44+
animated:NO];
45+
}
46+
```
47+
48+
当我们滑到一个新的图像去查看其高清图片时,这个方法将更新collectionView滑动的位置。这样一来,当用户查看完高清图回到这个界面的时候,高清图所对应的缩略图将会显示在界面上,方便用户获知自己浏览的位置以及继续往下浏览。
49+
50+
`#import`这些必要的数据模型的头文件并追加一下两个私有属性:
51+
52+
```
53+
@interface FRPFullSizePhotoViewController () <UIPageViewControllerDataSource, UIPageViewControllerDelegate>
54+
//Private assignment
55+
@property (nonatomic, strong) NSArray *photoModeArray;
56+
//Private properties
57+
@property (nonatomic, strong) UIPageViewController *pageViewController;
58+
@end
59+
```
60+
61+
`photoModelArray`是共有的只读属性,但是内部可读写。第二个属性是我们的子视图控制器。我们这样来初始化:
62+
63+
```
64+
- (instancetype)initWithPhotoModels:(NSArray *)photoModelArray currentPhotoIndex:(NSInteger)photoIndex{
65+
self = [self init];
66+
if (!self) return nil;
67+
68+
//Initialized, read-only properties
69+
self.photoModelArray = photoModelArray;
70+
71+
//Configure self
72+
self.title = [self.photoModelArray[photoIndex] photoName];
73+
74+
//ViewControllers
75+
self.pageViewController = [UIPageViewController alloc]
76+
initWithTransitionStyle:UIPageViewControlerTransitionStyleScroll
77+
navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal
78+
options:@{ UIPageViewControllerInterPageSpacingKey: @(30)};
79+
self.pageViewController.dataSource = self;
80+
self.pageViewController.delegate = self;
81+
[self addchildViewController:self.pageViewController];
82+
83+
[self.pageViewController setViewController:@[[self photoViewControllerForIndex:photoIndex]]
84+
direction:UIPageViewControllerNavigationDirectionForward
85+
animated:NO completion:nil ];
86+
87+
return self;
88+
}
89+
```
90+
91+
赋值属性、设置标题、配置我们的`pageViewController`,一切都非常无聊,我们的viewDidLoad方法也同样简单。
92+
93+
```
94+
- (void)viewDidLoad{
95+
[super viewDidLoad];
96+
97+
self.view,backGroundColor = [UIColor blackColor];
98+
99+
self.pageViewController.view.frame = self.view.bounds;
100+
101+
[self.view addSubView:self.pageViewController.view];
102+
}
103+
```
104+
我要指出的是,简便起见,在我的应用里我禁用了横向展示,因为这不是一本关于`autoresizingMask`或者`autoLayout`的书。你可以通过[Eria Sadun的书]()了解更多关于`autoLayout`方面的细节。

chapter5/basic_of_functionalReactivePixels.md

Lines changed: 59 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -145,30 +145,30 @@ self.apiHelper = [[PXAPIHelper alloc]
145145
+ (RACSignal *)importPhotos{
146146
RACReplaySubject * subject = [RACReplaySubject subject];
147147
NSURLRequest * request = [self popularURLRequest];
148-
[NSURLConnection sendAsynchronousRequest:request
149-
queue:[NSOperationQueue mainQueue]
148+
[NSURLConnection sendAsynchronousRequest:request
149+
queue:[NSOperationQueue mainQueue]
150150
completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError){
151151
if (data) {
152152
id results = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
153-
153+
154154
[subject sendNext:[[[results[@"photos"] rac_sequence] map:^id(NSDictionary *photoDictionary){
155155
FRPPhotoModel * model = [FRPPhotoModel new];
156-
156+
157157
[self configurePhotoModel:model withDictionary:photoDictionary];
158158
[self downloadThumbnailForPhotoModel:model];
159-
159+
160160
return model;
161161
}] array];
162-
162+
163163
[subject sendCompleted];
164164
}
165165
else{
166166
[subject sendError:connectionError];
167167
}
168168
}];
169-
169+
170170
return subject;
171-
171+
172172
}
173173
```
174174

@@ -184,7 +184,7 @@ self.apiHelper = [[PXAPIHelper alloc]
184184

185185
这是你看到的异步操作中,一个非常普通的模式。
186186

187-
1. 创建一个RACSubject.
187+
1. 创建一个RACSubject.
188188
2. 从异步调用的完成block中向RACSubject传送结果值。
189189
3. 立即返回这个RACSubject对象
190190

@@ -196,10 +196,10 @@ URL请求的构造方法看起来应该是这样的:
196196

197197
```
198198
+ (NSURLRequest *)popularURLRequest {
199-
return [AppDelegate.apiHelper urlRequestForPhotoFeature:PXAPIHelperPhotoFeaturePopular
199+
return [AppDelegate.apiHelper urlRequestForPhotoFeature:PXAPIHelperPhotoFeaturePopular
200200
resultsPerPage:100 page:0
201-
photoSize:PXPhotoModelSizeThumbnail
202-
sortOrder:PXAPIHelperSortOrderRating
201+
photoSize:PXPhotoModelSizeThumbnail
202+
sortOrder:PXAPIHelperSortOrderRating
203203
except:PXPhotoModelCategoryNude];
204204
}
205205
```
@@ -211,10 +211,10 @@ if(data){
211211
[subject sendNext:[[[results[@"photos"] rac_sequence] map:^id (NSDictionary *photoDictionary){
212212
FRPPhotoModel *model = [FRPPhotoModel new];
213213
[self donwloadThumbnailForPhotoModel:model];
214-
214+
215215
return model;
216216
}] array]];
217-
217+
218218
[subject sendCompleted];
219219
}
220220
else{
@@ -263,9 +263,9 @@ else{
263263
photomodel.identifier = dictionary[@"id"];
264264
photomodel.photographerName = dictionary[@"user"][@"username"];
265265
photomodel.rating = dictionary[@"rating"];
266-
266+
267267
photomodel.thumbnailURL = [self urlForImageSize:3 inArray:dictionary[@"images"]];
268-
268+
269269
//Extended attributes fetched with subsequent request
270270
if (dictionary[@"comments_count"]){
271271
photomodel.fullsizedURL = [self urlForImageSize:4 inArray:dictionary[@"images"]];
@@ -299,7 +299,7 @@ else{
299299
- 第一步,我们过滤掉那些`size`字段不匹配要求的字典。
300300
- 然后,将这些符合要求的字典做一次映射来提取字典中`url`字段的内容。
301301
- 最后,我们获得一个NSString 对象的序列,把它转化为数组,然后返回`firstObject`.
302-
302+
303303
> 这里插图一个
304304
305305
在ReactiveCocoa中类似上面的链式调用非常常见。值从`rac_sequence`推送到`filter:`方法中,最后推送到`map:`方法里。最后调用序列`rac_sequence``array`方法,将序列的结果转化为`array`.
@@ -309,10 +309,10 @@ else{
309309
```
310310
+ (void)downloadThumbnailForPhotoModel:(FRPPhotoModel *)photoModel{
311311
NSAssert(photoModel.thumbnailURL, @"Thumbnail URL must not be nil");
312-
312+
313313
NSURLRequest * request = [NSURLRequest requestWithURL:[NSURL URLWithString:photoModel.ThumbnailURL]];
314-
[NSURLConnection sendAsynchronousRequest:request
315-
queue:[NSOperationQueue mainQueue]
314+
[NSURLConnection sendAsynchronousRequest:request
315+
queue:[NSOperationQueue mainQueue]
316316
completionHandler:^(NSURLResponse *response, NSData *data, NSError * connectionError){
317317
photoModel.thumbnailData = data;
318318
}];
@@ -336,20 +336,20 @@ static NSString * CellIdentifier = @"Cell";
336336
337337
- (void)viewDidLoad{
338338
[super ViewDidLoad];
339-
339+
340340
//Configure self
341341
self.title = @"Popular on 500px";
342-
342+
343343
//Configure View
344344
[self.collectionView registerClass:[FRPCell class] forCellWithReuseIdentifier:CellIdentifier];
345-
345+
346346
//Reactive Stuff
347347
@weakify(self);
348348
[RACObserver(self, photosArray) subscribeNext:^(id x){
349349
@strongify(self);
350350
[self.collectionView reloadData];
351351
}];
352-
352+
353353
//Load data
354354
[self loadPopularPhotos];
355355
}
@@ -396,7 +396,7 @@ static NSString * CellIdentifier = @"Cell";
396396
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
397397
FRPCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath];
398398
[cell setPhotoModel:self.photosArray[indexPath.row]];
399-
399+
400400
return cell;
401401
}
402402
```
@@ -423,23 +423,23 @@ static NSString * CellIdentifier = @"Cell";
423423
424424
@end
425425
```
426-
426+
427427
这里有两个属性:一个图片视图和一个订阅者。图片视图是弱引用,因为它属于父视图(这是UICollectionViewCell的一个标准的用法),我们将实例化并赋值给imageView。接下来的属性是一个订阅,当使用ReactiveCocoa来设置图像视图的图像属性时,我们将接触到它。注意它必须是强引用而非弱引用否则你会得到一个运行时的异常。
428428

429429
```
430430
- (id)initWithFrame:(CGRect)frame{
431431
self = [super initWithFrame:frame];
432432
if(!self) return nil;
433-
433+
434434
//Configure self
435435
self.backgroundColor = []UIColor darkGrayColor];
436-
436+
437437
//Configure subviews
438438
UIImageView * imageView = [[UIImageView alloc] initWithFrame:self.bounds];
439439
imageView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
440440
[self.contentView addsubView:imageView];
441441
self.imageView = imageView;
442-
442+
443443
return self;
444444
}
445445
```
@@ -449,7 +449,7 @@ static NSString * CellIdentifier = @"Cell";
449449

450450
```
451451
- (void)setPhotoModel:(FRPPhotoModel *)photoModel{
452-
self.subscription = [[[RACObserver(photoModel, thumbnailData)
452+
self.subscription = [[[RACObserver(photoModel, thumbnailData)
453453
filter:^ BOOL (id value){
454454
return value != nil;
455455
}] map:^id (id value){
@@ -463,11 +463,11 @@ static NSString * CellIdentifier = @"Cell";
463463

464464
1. 当它没有接受一个新的值时,我们想延迟处理。
465465
2. 信号的订阅通常是冷信号,除非有人订阅他(信号),否则信号不会起作用。
466-
466+
467467
`setKeyPath:onObject:``RACSignal`的一个方法:绑定最新的信号的值给对象的关键路径。在这里我们在一个级联的信号上调用了这个方法,让我们来仔细看看:
468468

469469
```
470-
[[RACObserver (photoModel, thumbnailData)
470+
[[RACObserver (photoModel, thumbnailData)
471471
filter:^BOOL (id value){
472472
return value != nil;
473473
}] map:^ id (id value){
@@ -477,4 +477,30 @@ static NSString * CellIdentifier = @"Cell";
477477

478478
> 插入一个插图
479479
480-
Page47--待续
480+
信号由`RACObserver`这个C的宏生成,这个宏简单地返回一个监控目标对象关键路径值变化的信号。在我们这个例子中,我们的目标对象是`photoModel`,关键路径为`thumbnailData`属性。我们过滤掉所有的nil值,然后对过滤后的值做映射:把NSData实例转为UIImage对象。
481+
482+
注意,把NSData实例转化为UIImage的这个映射仅在小图上可以很好地运行,如果频繁地做这个映射或者作用到大图上会引起性能问题。理想的情况下,我们会缓存这些已经解压的图像以避免每一次都重复计算。这个技术不是本书所讨论的范畴,但我们将使用另一个通过ReactiveCocoa来实现的方法。
483+
484+
thumbnailData属性根本不需要在这里设置,他可以在稍后的某个时间在应用的其他部分来完成设置,然后cell的图像就会像魔术一般更新。
485+
486+
可以让我们稍微突破一下Model-View-Controller模式好吗?只是一点点的不守规矩。幸运的是,下一章我们将看到无处不在的MVC模式的困境,所以我们不必担心这一点点的突破,一点点的改进。
487+
488+
上面提到的`setKeyPath:onObject:`方法中,一旦`onObject:`对象被释放,他的订阅也会被自动取消。我们的cell实例是被collectionView所复用的,因此在复用的时候,我们需要取消cell上各组件的订阅。我们可以通过重写`UICollectionViewCell`的下列方法达成:
489+
490+
```
491+
- (void)perpareForReuse {
492+
[super prepareForReuse];
493+
494+
[self.subscription dispose], self.subscription = nil;
495+
}
496+
497+
```
498+
499+
这个方法在Cell被复用之前调用。如果现在运行我的应用,我们可以看到下面的结果:
500+
501+
> 插图
502+
503+
太好了!我们可以通过滚动视图来证实我们手动处理订阅的有效性。
504+
505+
506+

0 commit comments

Comments
 (0)