ReactiveCocoa 学习笔记

  1 //
  2 //  RootViewController.m
  3 //  SimpleWeather
  4 //
  5 //  Created by TBXark on 15-4-17.
  6 //  Copyright (c) 2015年 Ryan Nystrom. All rights reserved.
  7 //
  8
  9 #import "RootViewController.h"
 10 #import <ReactiveCocoa.h>
 11 #import <TSMessage.h>
 12 #import "ViewModel.h"
 13
 14 @interface RootViewController ()<UIGestureRecognizerDelegate>
 15
 16 @property (nonatomic,strong) UITextField *textfield;
 17 @property (nonatomic,strong) RACSubject *gestureRecognizerIsRunningSubject;
 18 @property (nonatomic,strong) RACSubject *gestureRecognizerValueSubject;
 19 @property (nonatomic,strong) UIView *someView;
 20
 21 @property (nonatomic,strong) ViewModel *viewModel;
 22 @property(assign) NSUInteger scoreUpdates;
 23
 24
 25 @end
 26
 27 static NSUInteger const kMaxUploads = 5;
 28
 29
 30 @implementation RootViewController
 31
 32 - (void)viewDidLoad {
 33     [super viewDidLoad];
 34
 35
 36     //   模式
 37     //
 38     //   在ReactiveCocoa中有三种基本的模式:责任链、分割和组合模式(chaining, splitting, and combining)在ReactiveCocoa中的核心是signal:(信号),它表示不断变化的状态。当我们使用chain、split和combine时,实际上我们就是在操作这些signal
 39
 40 //*************************************************************************//
 41
 42     //    责任链:chaining
 43     //     Chaining是在ReactiveCocoa最常用的模式:将一个已有的signal转换为一个新的signal。常用的操作是创建一个新的signal,再对它使用filter:、map:或startWith:等方法。
 44
 45     CGFloat width = self.view.bounds.size.width;
 46     _textfield = [[UITextField alloc] initWithFrame:CGRectMake(40,30, width-80, 30)];
 47     _textfield.textAlignment = NSTextAlignmentCenter;
 48     //    [self.view addSubview:_textfield];
 49
 50     RAC(self,textfield.text) = [[[RACSignal interval:1 onScheduler:[RACScheduler mainThreadScheduler]] startWith:[NSDate date]] map:^id(NSDate *value) {
 51         NSDateFormatter *df = [[NSDateFormatter alloc] init];
 52         df.dateFormat = @"yyyy-MM-dd hh:mm:ss";
 53         return [df stringFromDate:value];
 54     }];
 55
 56 //*************************************************************************//
 57
 58     //    分割:Splitting
 59     //    Splitting与chaining比较类似,也是将signal转换为其它的sginal,不同之处在于,Splitting会重复使用中间的signals。Splitting看起来要复杂些,其实也就是一个signals使用多次罢了。
 60
 61     UITextField *dateTextField = [[UITextField alloc] initWithFrame:CGRectMake(40, 70, width-80, 30)];
 62     UITextField *timeTextField = [[UITextField alloc] initWithFrame:CGRectMake(40, 100, width-80, 30)];
 63     //    [self.view addSubview:dateTextField];
 64     //    [self.view addSubview:timeTextField];
 65     dateTextField.textAlignment = NSTextAlignmentCenter;
 66     timeTextField.textAlignment = NSTextAlignmentCenter;
 67
 68
 69     RACSignal *mainSingal = [[[RACSignal interval:1 onScheduler:[RACScheduler mainThreadScheduler]] startWith:[NSDate date]] map:^id(NSDate *value) {
 70         NSDateComponents *dateComponents = [[NSCalendar currentCalendar] components:NSMinuteCalendarUnit | NSSecondCalendarUnit fromDate:value];
 71         return dateComponents;
 72     }];
 73
 74     //    deliverOn:[RACScheduler mainThreadScheduler]];
 75     RAC(dateTextField,text) = [[mainSingal map:^id(NSDateComponents *value) {
 76         return [NSString stringWithFormat:@"%u-%u-%u",value.year,value.month,value.day];
 77     }] deliverOn:[RACScheduler mainThreadScheduler]];
 78     RAC(timeTextField,text) = [[mainSingal map:^id(NSDateComponents *value) {
 79         return [NSString stringWithFormat:@"%u:%u:%u",value.hour,value.minute,value.second];
 80     }] deliverOn:[RACScheduler mainThreadScheduler]];
 81
 82 //*************************************************************************//
 83
 84     //    组合:combining
 85     //    combining就是将几个signal结合起来创建出一个新的signal。
 86     //    组合的信号(combined signal)只会在所有的输入至少都有一个值的时候才会发送它的第一个值
 87     UITextField *userName = [[UITextField alloc] initWithFrame:CGRectMake(40, 130, width-80, 30)];
 88     UITextField *password = [[UITextField alloc] initWithFrame:CGRectMake(40, 165, width-80, 30)];
 89     UIButton *loginButton = [[UIButton alloc] initWithFrame:CGRectMake(40, 200, width-80, 30)];
 90     userName.backgroundColor = [UIColor grayColor];
 91     password.backgroundColor = [UIColor grayColor];
 92     loginButton.backgroundColor = [UIColor darkGrayColor];
 93     [loginButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
 94     [loginButton setTitle:@"Login" forState:UIControlStateNormal];
 95     [loginButton setTitle:@"Waiting" forState:UIControlStateDisabled];
 96 //    [self.view addSubview:userName];
 97 //    [self.view addSubview:password];
 98 //    [self.view addSubview:loginButton];
 99
100     //         在这里,我们将“登录”按钮的enable状态绑定到使用combineLatest:reduce:方法创建的signal上。
101     //         这个方法的第二个参数是一个block,这个block的参数是combineLatest中的参数的最新值的组合。我们将两个文本框的text signal一起传到combineLatest,在reduce的block中,该block也就会接收到两个NSString的参数.
102     //         这个block的工作就是将两个参数值组合起来生成一个值,然后返回。
103     RAC(loginButton,enabled) = [RACSignal combineLatest:@[userName.rac_textSignal,password.rac_textSignal] reduce:^id(NSString *userName, NSString *password) {
104         return @(userName.length >= 6 && password.length >= 6);
105     }];
106
107 //*************************************************************************//
108
109     //RACSubjects
110     //
111     //    RACSubjects则充当了非reactive和 reactive代码的桥梁。
112     //    RACSubject是能够手动发送新值的signal。
113     //     虽然RACSubjects是非reactive代码与ReactiveCocoa代码的桥梁,但过分滥用也是有风险的。当我们能够通过chaining signals完成任务的话,就不要依赖于RACSubjects的值。
114     //    虽然subscriptions很有用,执行副作用也是必要的,但要小心过度使用。它们就是可变的变量、状态,这些正是ReactiveCocoa所避免的。在能够通过绑定属性映射signals完成任务的时候,就不要使用RACSubjects。
115
116     self.gestureRecognizerIsRunningSubject = [RACSubject subject];
117     self.gestureRecognizerValueSubject = [RACSubject subject];
118     self.someView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 40, 40)];
119     self.someView.layer.cornerRadius = 20;
120     self.someView.backgroundColor = [UIColor darkGrayColor];
121
122     UIPanGestureRecognizer *tap = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(gestureRecognizerReceivedTouch:)];
123     tap.delegate = self;
124     RAC(self,someView.frame) = [self.gestureRecognizerValueSubject map:^id(NSValue *value) {
125         CGPoint location = [value CGPointValue];
126         CGFloat size = 40.0f;
127         return [NSValue valueWithCGRect:CGRectMake(location.x - size/2.0f, location.y - size/2.0f, size, size)];
128     }];
129
130 //    [self.view addSubview:self.someView];
131 //    [self.view addGestureRecognizer:tap];
132
133
134 //*************************************************************************//
135
136     //RACCommand
137     //
138     //     RACCommand类用于表示事件的执行,一般来说是在UI上的某些动作来触发这些事件,比如点击一个按钮。RACCommand的实例能够决定是否可以被执行,这个特性能反应在UI上,而且它能确保在其不可用时不会被执行。通常,当一个命令可以执行时,会将它的属性allowsConcurrentExecution设置为它的默认值:NO,从而确保在这个命令已经正在执行的时候,不会同时再执行新的操作。命令执行的返回值是一个RACSignal,因此我们能对该返回值进行next:,completed或error:
139
140
141 //    RAC给UITableViewCell提供了一个方法:rac_prepareForReuseSignal,它的作用是当Cell即将要被重用时,告诉Cell。想象Cell上有多个button,Cell在初始化时给每个button都addTarget:action:forControlEvents,被重用时需要先移除这些target,下面这段代码就可以很方便地解决这个问题:
142 //    [[[self.cancelButton
143 //       rac_signalForControlEvents:UIControlEventTouchUpInside]
144 //      takeUntil:self.rac_prepareForReuseSignal]
145 //     subscribeNext:^(UIButton *x) {
146 //         // do other things
147 //     }];
148
149
150
151 //*************************************************************************//
152
153     //概念
154
155     //Streams 抽象基类
156     //    -Signals 信号
157     //    -Sequences 信号集合
158
159     //RACCommand 命令?
160     //Schedulers 多线程?
161
162     //Subscription 订阅 接收 -subscribeNext: -subscribeError: -subscribeCompleted:
163     //Injecting effects     注入效果 -doNext: -doError: -doCompleted:
164     //Mapping 信号转换
165     //Filtering 信号过滤
166     //Concatenating 信号拼接
167     //Flattening 数组拼接
168     //Mapping and flattening 先Map在Flatten
169
170     [self reactiveCocoaBasicOperation];
171     [self reactiveCocoaMVVM];
172
173 }
174
175 - (void)reactiveCocoaMVVM
176 {
177     CGFloat width = self.view.bounds.size.width;
178     UITextField *nameField = [[UITextField alloc] initWithFrame:CGRectMake(40, 40, width-80, 40)];
179     [self.view addSubview:nameField];
180
181     UILabel *scoreField = [[UILabel alloc] initWithFrame:CGRectMake(40, 100, width-80, 40)];
182     [self.view addSubview:scoreField];
183
184     UIStepper *scoreStepper = [[UIStepper alloc] initWithFrame:CGRectMake(100, 160, width-200, 40)];
185     [self.view addSubview:scoreStepper];
186
187     UIButton *uploadButton = [[UIButton alloc] initWithFrame:CGRectMake(100, 210, width-200, 40)];
188     [self.view addSubview:uploadButton];
189     [uploadButton setTitle:@"Upload" forState:UIControlStateNormal];
190     uploadButton.backgroundColor = [UIColor darkGrayColor];
191
192
193
194     self.viewModel = [ViewModel new];
195
196     //using with @strongify(self) this makes sure that self isn‘t retained in the blocks
197     //this is declared in RACEXTScope.h
198     @weakify(self);
199
200     //Start Binding our properties
201     RAC(nameField,text) = [RACObserve(self.viewModel, playerName) distinctUntilChanged];
202
203     [[nameField.rac_textSignal distinctUntilChanged] subscribeNext:^(NSString *x) {
204         //this creates a reference to self that when used with @weakify(self);
205         //makes sure self isn‘t retained
206         @strongify(self);
207         self.viewModel.playerName = x;
208     }];
209
210     //the score property is a double, RC gives us updates as NSNumber which we just call
211     //stringValue on and bind that to the scorefield text
212     RAC(scoreField,text) = [RACObserve(self.viewModel,points) map:^id(NSNumber *value) {
213         return [value stringValue];
214     }];
215
216     //Setup bind the steppers values
217     scoreStepper.value = self.viewModel.points;
218     RAC(scoreStepper,stepValue) = RACObserve(self.viewModel,stepAmount);
219     RAC(scoreStepper,maximumValue) = RACObserve(self.viewModel,maxPoints);
220     RAC(scoreStepper,minimumValue) = RACObserve(self.viewModel,minPoints);
221     //bind the hidden field to a signal keeping track if
222     //we‘ve updated less than a certain number times as the view model specifies
223     RAC(scoreStepper,hidden) = [RACObserve(self,scoreUpdates) map:^id(NSNumber *x) {
224         @strongify(self);
225         return @(x.intValue >= self.viewModel.maxPointUpdates);
226     }];
227
228     //only take the maxPointUpdates number of score updates
229     //skip 1 because we don‘t want the 1st value provided, only changes
230     [[[RACObserve(scoreStepper,value) skip:1] take:self.viewModel.maxPointUpdates] subscribeNext:^(id newPoints) {
231         @strongify(self);
232         self.viewModel.points = [newPoints doubleValue];
233         self.scoreUpdates++;
234     }];
235
236     //this signal should only trigger if we have "bad words" in our name
237     [self.viewModel.forbiddenNameSignal subscribeNext:^(NSString *name) {
238         @strongify(self);
239         UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Forbidden Name!"
240                                                         message:[NSString stringWithFormat:@"The name %@ has been forbidden!",name]
241                                                        delegate:nil
242                                               cancelButtonTitle:@"Ok"
243                                               otherButtonTitles:nil];
244         [alert show];
245         self.viewModel.playerName = @"";
246     }];
247
248     //let the upload(save) button only be enabled when the view model says its valid
249     RAC(uploadButton,enabled) = self.viewModel.modelIsValidSignal;
250
251     //set the control action for our button to be the ViewModels action method
252     [uploadButton addTarget:self.viewModel
253                           action:@selector(uploadData:)
254                 forControlEvents:UIControlEventTouchUpInside];
255
256     //we can subscribe to the same thing in multiple locations
257     //here we skip the first 4 signals and take only 1 update
258     //and then disable/hide certain UI elements as our app
259     //only allows 5 updates
260     [[[[uploadButton rac_signalForControlEvents:UIControlEventTouchUpInside]
261        skip:(kMaxUploads - 1)] take:1] subscribeNext:^(id x) {
262         @strongify(self);
263         nameField.enabled = NO;
264         scoreStepper.hidden = YES;
265         uploadButton.hidden = YES;
266     }];
267
268 }
269
270 - (void)reactiveCocoaBasicOperation
271 {
272     //基础操作
273
274
275 //    1.响应与信号
276
277 //##Subscription 订阅
278 //     -subscribe…
279 //    The -subscribe… methods give you access to the current and future values in a signal:
280 #if 0
281     RACSignal *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence.signal;
282     [letters subscribeNext:^(NSString *x) {
283         NSLog(@"%@", x);
284     }];
285 #endif
286
287 #if 0
288     //    For a cold signal, side effects will be performed once per subscription:
289     __block unsigned subscriptions = 0;
290
291     RACSignal *loggingSignal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
292         subscriptions++;
293         NSLog(@"I am here");
294         [subscriber sendCompleted];
295         return nil;
296     }];
297     // Outputs:
298     // subscription 1
299     [loggingSignal subscribeCompleted:^{
300         NSLog(@"subscription %u", subscriptions);
301     }];
302
303     // Outputs:
304     // subscription 2
305     [loggingSignal subscribeCompleted:^{
306         NSLog(@"subscription %u", subscriptions);
307     }];
308 #endif
309
310
311 //##Injecting effects 注入
312 //    -doNext  -doCompleted -doError
313 //    The -do… methods add side effects to a signal without actually subscribing to it:
314 //    添加事件,并不需要信号真正被订阅
315 #if 0
316
317     __block unsigned subscriptions = 0;
318
319     RACSignal *loggingSignal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
320         subscriptions++;
321         [subscriber sendCompleted];
322 //        [subscriber sendNext:nil];
323         return nil;
324     }];
325
326     // Does not output anything yet
327     loggingSignal = [loggingSignal doCompleted:^{
328         NSLog(@"about to complete subscription %u", subscriptions);
329     }];
330
331     loggingSignal = [loggingSignal doNext:^(id x) {
332         NSLog(@"about to next subscription %u", subscriptions);
333     }];
334
335     [loggingSignal subscribeNext:^(id x) {
336         NSLog(@"subscription %u",subscriptions);
337     }];
338     // Outputs:
339     // about to complete subscription 1
340     // subscription 1
341     [loggingSignal subscribeCompleted:^{
342         NSLog(@"subscription %u", subscriptions);
343     }];
344 #endif
345
346 //   2.信号流变换    These operators transform a single stream into a new stream.
347
348 //## Mapping 映射
349 //    The -map: method is used to transform the values in a stream, and create a new stream with the results:
350 //    将流的值转换为新的值后再次放入流中
351 #if 0
352     RACSequence *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence;
353
354     // Contains: AA BB CC DD EE FF GG HH II
355     RACSequence *mapped = [letters map:^(NSString *value) {
356         return [value stringByAppendingString:value];
357     }];
358
359     [mapped.signal subscribeNext:^(NSString *str) {
360         NSLog(@"%@",str);
361     }];
362 #endif
363
364
365 //##Filtering 过滤
366 //     -filter:
367 //    使用blcok过滤流,使得能通过测试的流通过
368 #if 0
369     RACSequence *numbers = [@"1 2 3 4 5 6 7 8 9" componentsSeparatedByString:@" "].rac_sequence;
370
371     // Contains: 2 4 6 8
372     RACSequence *filtered = [numbers filter:^ BOOL (NSString *value) {
373         return (value.intValue % 2) == 0;
374     }];
375     [filtered.signal subscribeNext:^(id x) {
376         NSLog(@"%@",x);
377     }];
378 #endif
379
380
381 //    3.流的组合   These operators combine multiple streams into a single new stream
382
383 //##Concatenating 连接
384 //     -concat:
385 //    将一个信号添加到另一个信号后面
386 #if 0
387     RACSequence *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence;
388     RACSequence *numbers = [@"1 2 3 4 5 6 7 8 9" componentsSeparatedByString:@" "].rac_sequence;
389
390     // Contains: A B C D E F G H I 1 2 3 4 5 6 7 8 9
391     RACSequence *concatenated = [letters concat:numbers];
392     [concatenated.signal subscribeNext:^(id x) {
393         NSLog(@"%@",x);
394
395     }];
396
397 #endif
398
399 //##Flattening 压扁
400 //     -flatten
401 //    Flattn操作应用在"流中的流",将其组合并形成一个新的流
402
403 #if 0
404
405     RACSubject *letters = [RACSubject subject];
406     RACSubject *numbers = [RACSubject subject];
407     RACSignal *signalOfSignals = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
408 //        其中letters为信号,letters中的值为信号中的信号,如果不进行压扁,者不能获得真正的信号
409         [subscriber sendNext:letters];
410         [subscriber sendNext:numbers];
411         [subscriber sendCompleted];
412         return nil;
413     }];
414
415     RACSignal *flattened = [signalOfSignals flatten];
416
417     // Outputs: A 1 B C 2
418     [flattened subscribeNext:^(NSString *x) {
419         NSLog(@"%@", x);
420     }];
421
422     //Outputs : <RACSubject: 0x7a1f9ff0> name:
423     //          <RACSubject: 0x7a17fdd0> name:
424
425     [signalOfSignals subscribeNext:^(id x) {
426         NSLog(@"%@",x);
427     }];
428
429     [letters sendNext:@"A"];
430     [numbers sendNext:@"1"];
431     [letters sendNext:@"B"];
432     [letters sendNext:@"C"];
433     [numbers sendNext:@"2"];
434
435 #endif
436
437
438 //##Mapping and flattening
439 //    -flattenMap:
440 //     it‘s -map: followed by -flatten.
441
442 #if 0
443     RACSequence *numbers = [@"1 2 3 4 5 6 7 8 9" componentsSeparatedByString:@" "].rac_sequence;
444
445     // Contains: 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9
446     RACSequence *extended = [numbers flattenMap:^(NSString *num) {
447         return @[ num, num ].rac_sequence;
448     }];
449
450     // Contains: 1_ 3_ 5_ 7_ 9_
451     RACSequence *edited = [numbers flattenMap:^(NSString *num) {
452         if (num.intValue % 2 == 0) {
453             return [RACSequence empty];
454         } else {
455             NSString *newNum = [num stringByAppendingString:@"_"];
456             return [RACSequence return:newNum];
457         }
458     }];
459 //    [extended.signal subscribeNext:^(id x) {
460 //        NSLog(@"%@",x);
461 //    }];
462
463     [edited.signal subscribeNext:^(id x) {
464         NSLog(@"%@",x);
465     }];
466
467 #endif
468
469 //    4.信号的组合 These operators combine multiple signals into a single new RACSignal.
470
471 //##Sequencing
472 //    -then:
473 //    starts the original signal, waits for it to complete, and then only forwards the values from a new signal:
474 #if 0
475     RACSignal *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence.signal;
476     // The new signal only contains: 1 2 3 4 5 6 7 8 9
477     // But when subscribed to, it also outputs: A B C D E F G H I
478     RACSignal *sequenced = [[letters
479                              doNext:^(NSString *letter) {
480                                  NSLog(@"%@", letter);
481                              }]
482                             then:^{
483                                 return [@"1 2 3 4 5 6 7 8 9" componentsSeparatedByString:@" "].rac_sequence.signal;
484                             }];
485
486     [sequenced subscribeNext:^(id x) {
487         NSLog(@"%@",x);
488     }];
489 #endif
490
491 //##Merging 合并
492 //     +merge:
493 //    将多个信号转发到一个信号流,一旦信号到达立即转发
494 #if 0
495
496     RACSubject *letters = [RACSubject subject];
497     RACSubject *numbers = [RACSubject subject];
498     RACSignal *merged = [RACSignal merge:@[ letters, numbers ]];
499
500     // Outputs: A 1 B C 2
501     [merged subscribeNext:^(NSString *x) {
502         NSLog(@"%@", x);
503     }];
504
505     [letters sendNext:@"A"];
506     [numbers sendNext:@"1"];
507     [letters sendNext:@"B"];
508     [letters sendNext:@"C"];
509     [numbers sendNext:@"2"];
510
511 #endif
512
513
514 //##Combining latest values
515 //+combineLatest:
516 //    方法将观察多个信号的变化,然后发送最新的值发生改变时
517 //    每个信号至少有一个值时才会转发,而merge是立即转发
518 #if 0
519     RACSubject *letters = [RACSubject subject];
520     RACSubject *numbers = [RACSubject subject];
521     RACSignal *combined = [RACSignal
522                            combineLatest:@[ letters, numbers ]
523                            reduce:^(NSString *letter, NSString *number) {
524                                return [letter stringByAppendingString:number];
525                            }];
526
527     // Outputs: B1 B2 C2 C3
528     [combined subscribeNext:^(id x) {
529         NSLog(@"%@", x);
530     }];
531
532     [letters sendNext:@"A"];
533     [letters sendNext:@"B"];
534     [numbers sendNext:@"1"];
535     [numbers sendNext:@"2"];
536     [letters sendNext:@"C"];
537     [numbers sendNext:@"3"];
538
539 #endif
540
541 //##Switching
542 //     -switchToLatest
543 //    作用与信号中的信号,并且转发最后的值
544 #if 0
545     RACSubject *letters = [RACSubject subject];
546     RACSubject *numbers = [RACSubject subject];
547     RACSubject *signalOfSignals = [RACSubject subject];
548
549     RACSignal *switched = [signalOfSignals switchToLatest];
550
551     // Outputs: A B 1 D
552     [switched subscribeNext:^(NSString *x) {
553         NSLog(@"%@", x);
554     }];
555
556     [signalOfSignals sendNext:letters];
557     [letters sendNext:@"A"];
558     [letters sendNext:@"B"];
559
560     [signalOfSignals sendNext:numbers];
561     [letters sendNext:@"C"];
562     [numbers sendNext:@"1"];
563
564     [signalOfSignals sendNext:letters];
565     [numbers sendNext:@"2"];
566     [letters sendNext:@"D"];
567
568 #endif
569
570 }
571
572 -(void)gestureRecognizerReceivedTouch:(UIPanGestureRecognizer *)recognizer {
573     if (recognizer.state == UIGestureRecognizerStateBegan) {
574         [self.gestureRecognizerIsRunningSubject sendNext:@(YES)];
575     }
576
577     else if (recognizer.state == UIGestureRecognizerStateChanged) {
578         [self.gestureRecognizerValueSubject sendNext:[NSValue valueWithCGPoint:[recognizer locationInView:self.view]]];
579     }
580
581     else if (recognizer.state == UIGestureRecognizerStateEnded) {
582         [self.gestureRecognizerIsRunningSubject sendNext:@(NO)];
583     }
584 }
585
586
587 - (void)didReceiveMemoryWarning {
588     [super didReceiveMemoryWarning];
589     // Dispose of any resources that can be recreated.
590 }
591
592 /*
593  #pragma mark - Navigation
594
595  // In a storyboard-based application, you will often want to do a little preparation before navigation
596  - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
597  // Get the new view controller using [segue destinationViewController].
598  // Pass the selected object to the new view controller.
599  }
600  */
601
602 @end
时间: 2024-10-03 04:52:53

ReactiveCocoa 学习笔记的相关文章

iOS开发ReactiveCocoa学习笔记(-)

学习 RAC 我们首先要了解 RAC 都有哪些类 RACSignal RACSubject RACSequence RACMulticastConnection RACCommand 在学习的时候写了一个小 demo 来分别介绍每个类的作用,gitHub 地址: https://github.com/SummerHH/ReactiveCocoa.git demo 的目录结构如下 RAC学习起来的特点 学习起来比较难 团队开发的时候需要谨慎使用 团队代码需要不断的评审,保证团队中所有人代码的风格一

iOS开发ReactiveCocoa学习笔记(四)

ReactiveCocoa常见操作方法介绍: demo地址:https://github.com/SummerHH/ReactiveCocoa.git p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #008400 } p.p2 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #008400; min-height: 13.0px }

iOS开发ReactiveCocoa学习笔记(二)

RAC 中常见的宏: 使用宏定义要单独导入 #import <RACEXTScope.h> 一. RAC(TARGET, [KEYPATH, [NIL_VALUE]]):用于给某个对象的某个属性绑定 只要文本框文字改变,就会修改label的文字 RAC(self.labelView,text) = _textField.rac_textSignal; 二. RACObserve(self, name):监听某个对象的某个属性,返回的是信号. [RACObserve(self.view, cen

ReactiveCocoa学习笔记

1. RACSignal使用步骤及底层实现 (1) 创建信号 (RACSignal底层实现:把didSubscribe保存到信号中,还不会触发.) createSignal的意义是,创建一个signal对象,并且把参数赋值给signal的名为didSubscribe的属性,这个block的参数是subscriber,返回RACDisposable. + (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscr

高大上函数响应式编程框架ReactiveCocoa学习笔记1 简介

原创文章,转载请声明出处哈. ReactiveCocoa函数响应式编程 一.简介 ReactiveCocoa(其简称为RAC)是函数响应式编程框架.RAC具有函数式编程和响应式编程的特性.它主要吸取了.Net的 Reactive Extensions的设计和实现. 函数式编程 (Functional Programming) 函数式编程也可以写N篇,它是完全不同于OO的编程模式,这里主要讲一下这个框架使用到的函数式思想. 1) 高阶函数:在函数式编程中,把函数当参数来回传递,而这个,说成术语,我

ReactiveCocoa学习笔记--用法

1.监测UI变量的变化 return 后把值传递下去. 1.1.输出 [self.usernameTextField.rac_textSignal subscribeNext:^(id x){ NSLog(@"%@", x); }]; 1.2.过滤->输出 [[self.usernameTextField.rac_textSignal filter:^BOOL(NSString*text){ return text.length > 3; }] subscribeNext:

vector 学习笔记

vector 使用练习: /**************************************** * File Name: vector.cpp * Author: sky0917 * Created Time: 2014年04月27日 11:07:33 ****************************************/ #include <iostream> #include <vector> using namespace std; int main

Caliburn.Micro学习笔记(一)----引导类和命名匹配规则

Caliburn.Micro学习笔记(一)----引导类和命名匹配规则 用了几天时间看了一下开源框架Caliburn.Micro 这是他源码的地址http://caliburnmicro.codeplex.com/ 文档也写的很详细,自己在看它的文档和代码时写了一些demo和笔记,还有它实现的原理记录一下 学习Caliburn.Micro要有MEF和MVVM的基础 先说一下他的命名规则和引导类 以后我会把Caliburn.Micro的 Actions IResult,IHandle ICondu

jQuery学习笔记(一):入门

jQuery学习笔记(一):入门 一.JQuery是什么 JQuery是什么?始终是萦绕在我心中的一个问题: 借鉴网上同学们的总结,可以从以下几个方面观察. 不使用JQuery时获取DOM文本的操作如下: 1 document.getElementById('info').value = 'Hello World!'; 使用JQuery时获取DOM文本操作如下: 1 $('#info').val('Hello World!'); 嗯,可以看出,使用JQuery的优势之一是可以使代码更加简练,使开