ReactiveCocoa basic knowledge, reactivecocoa
This article records some basic knowledge about ReactiveCocoa. If you do not know ReactiveCocoa related concepts, you can search online. RACSignal has many ways to subscribe to different event types, the ReactiveCocoa framework uses category to add signal to many basic UIKit controls.
1. Create a page layout first (preparation phase)
@interface ViewController ()@property(strong,nonatomic)UITextField *nameTextField;@property(strong,nonatomic)UILabel *contentLabel;@property(strong,nonatomic)UIButton *saveBtn;@end
If (self. nameTextField = nil) {self. nameTextField = [[UITextField alloc] init]; self. nameTextField. backgroundColor = [UIColor grayColor]; [self. view addSubview: self. nameTextField]; [self. nameTextField mas_makeConstraints: ^ (MASConstraintMaker * make) {make. top. mas_pointer to (self. view. mas_top ). with. offset (64); make. left. mas_pointer to (self. view. mas_left ). with. offset (0); make. right. mas_pointer to (self. view. mas_right ). with. offset (0); make. height. mas_defaults to (@ 40);}];} if (self. contentLabel = nil) {self. contentLabel = [[UILabel alloc] init]; self. contentLabel. backgroundColor = [UIColor blueColor]; [self. view addSubview: self. contentLabel]; [self. contentLabel mas_makeConstraints: ^ (MASConstraintMaker * make) {make. top. mas_pointer to (self. nameTextField. mas_bottom ). with. offset (10); make. left. mas_pointer to (self. view. mas_left ). with. offset (0); make. right. mas_pointer to (self. view. mas_right ). with. offset (0); make. height. mas_defaults to (@ 100);}];} if (self. saveBtn = nil) {self. saveBtn = [[UIButton alloc] init]; self. saveBtn. backgroundColor = [UIColor blackColor]; [self. saveBtn setTitle: @ "Submit" forState: UIControlStateNormal]; [self. view addSubview: self. saveBtn]; [self. saveBtn mas_makeConstraints: ^ (MASConstraintMaker * make) {make. top. mas_pointer to (self. contentLabel. mas_bottom ). with. offset (20); make. left. mas_pointer to (self. view. mas_left ). with. offset (30); make. right. mas_pointer to (self. view. mas_right ). with. offset (-30); make. height. mas_equalTo (@ 40);}];}
Note: saveBtn is the call code without adding events. You can use ReactiveCocoa to register events for it and bind corresponding operations;
Ii. ReactiveCocoa small instance (these are directly bound to viewDidLoad after the control is created)
1: rac_textSignal of UITextField, which generates a signal when the text changes
[self.nameTextField.rac_textSignal subscribeNext:^(id x) { self.contentLabel.text=x; }];
Effect: the input will immediately display the changed value, saving the previous listening operations;
2: filter condition Filtering
[[self.nameTextField.rac_textSignal filter:^BOOL(id value) { NSString *text=value; return text.length>3; }] subscribeNext:^(id x) { self.contentLabel.text=x; }];
Effect: only the input string with a length greater than 3 is displayed as the string content;
3: Split statement,Rac_textSignalAndFilterAllRACSignal
RACSignal *nameRacSignal=self.nameTextField.rac_textSignal; RACSignal *filteredName=[nameRacSignal filter:^BOOL(id value) { NSString *text=value; return text.length>3; }]; [filteredName subscribeNext:^(id x) { self.contentLabel.text=x; }];
Effect: the effect is the same as that in instance 2, but is defined separately;
4: All the above id types can be matched according to the actual situation
[[self.nameTextField.rac_textSignal filter:^BOOL(NSString *text){ return text.length > 3; }] subscribeNext:^(id x){ self.contentLabel.text=x; }];
Effect: here, the filter is of the NSString type. Other types can also be compared, such as int bool. The effect is as follows:
5: map changes the current value and passes it to the next
[[Self. nameTextField. rac_textSignal map: ^ id (NSString * text) {return @ (text. length) ;}] filter: ^ BOOL (NSNumber * length) {return [length intValue]> 3 ;}] subscribeNext: ^ (id x) {// remember to display the NSNumber self. contentLabel. text = [NSString stringWithFormat: @ "% @", x] ;}];
Effect: The result is displayed as follows:, 8, 9 ....., the string content is no longer displayed and has been modified by map to length. Therefore, the filter parameter is also changed. It can be used to convert it into an object.
6: verify the validity and modify the corresponding attributes (For details, refer to, macro definition RAC)
RACSignal * validUsernameSignal = [self. nameTextField. rac_textSignal map: ^ id (NSString * text) {return @ ([self isValidUsername: text]) ;}]; [[validUsernameSignal map: ^ id (NSNumber * userNameValid) {return [userNameValid boolValue]? [UIColor redColor]: [UIColor yellowColor];}] subscribeNext: ^ (UIColor * color) {self. contentLabel. backgroundColor = color;}]; The method code is as follows:-(BOOL) isValidUsername :( NSString *) userName {if ([userName isw.tostring: @ "wjy"]) {return true ;} else {return false ;}}
Effect: the background effect is changed only when the input string is wjy;
7: Application of macro RAC
RACSignal *validUsernameSignal = [self.nameTextField.rac_textSignal map:^id(NSString *text) { return @([self isValidUsername:text]); }]; RAC(self.contentLabel,backgroundColor)=[validUsernameSignal map:^id(NSNumber *userNameValid){ return[userNameValid boolValue] ? [UIColor redColor]:[UIColor yellowColor]; }];
Effect: This is a simplified method, which is directly implemented using the macro RAC. You can remove the subscribeNext: block from the current pipeline and use the RAC macro instead.
Note: RAC macros allow direct application of signal output to object attributes. The RAC macro has two parameters: the first is the object that needs to set the attribute value, and the second is the attribute name. Each time a signal generates a next event, the passed value is applied to this attribute.
8: Aggregation signal (reference)
RACSignal *validUsernameSignal = [self.usernameTextField.rac_textSignal map:^id(NSString *text) { return @([self isValidUsername:text]); }]; RACSignal *validPasswordSignal = [self.passwordTextField.rac_textSignal map:^id(NSString *text) { return @([self isValidPassword:text]); }];
RACSignal *signUpActiveSignal = [RACSignal combineLatest:@[validUsernameSignal, validPasswordSignal] reduce:^id(NSNumber*usernameValid, NSNumber *passwordValid){ return @([usernameValid boolValue]&&[passwordValid boolValue]); }];
[signUpActiveSignal subscribeNext:^(NSNumber*signupActive){ self.signInButton.enabled =[signupActive boolValue]; }];
Note: This method of RACsignal can aggregate any number of signals. The parameters of reduce block are related to each source signal. ReactiveCocoa has a tool class RACBlockTrampoline that internally processes variable parameters of reduce blocks.
Effect: The above code is used.CombineLatest: reduce:MethodValidUsernameSignalAndValidPasswordSignalThe generated latest values are aggregated and a new signal is generated. Each time either of the two source signals generates a new value, the reduce block will execute and the return value of the block will be sent to the next signal.
9: UIButton event rac_signalForControlEvents
[[self.saveBtn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) { NSLog(@"button clicked"); }];
Effect: Click Event Response
10: doNext
[[[self.saveBtn rac_signalForControlEvents:UIControlEventTouchUpInside] doNext:^(id x) { self.contentLabel.backgroundColor=[UIColor greenColor]; } ] subscribeNext:^(id x) { NSLog(@"button clicked"); self.contentLabel.backgroundColor=[UIColor redColor]; }];
Note: doNext is directly behind the button click event. In addition, doNext: block does not return values. Because it is an additional operation and does not change the event itself, the function is as follows: The doNext: block above sets the button as unclickable to hide the logon Failure prompt. In subscribeNext: block, set the button to clickable and decide whether to display the failure prompt Based on the logon result. The instance is as follows:
Instance: [[[self. saveBtn rac_signalForControlEvents: UIControlEventTouchUpInside] doNext: ^ (id x) {self. signInButton. enabled = NO; self. signInFailureText. hidden = YES;}] flattenMap: ^ id (id x) {return [self signInSignal];}] subscribeNext: ^ (NSNumber * signedIn) {self. signInButton. enabled = YES; BOOL success = [signedIn boolValue]; self. signInFailureText. hidden = success; if (success) {[self defined mseguewithidentifier: @ "signInSuccess" sender: self] ;}}];