ReactiveCocoa 學(xué)習(xí) 一

ReactiveCocoa 框架,在剛聽說過這個(gè)框架時(shí),我便在github上搜索了一下,star的數(shù)量確實(shí)讓我震驚了一下。查詢資料了解后,感覺自己入行沒多久,感覺這個(gè)框架高深莫測,不敢涉獵。
這個(gè)周末的閑暇之余,我決定學(xué)習(xí)一下這個(gè)框架并寫個(gè)登錄demo。

開始了。
首先來導(dǎo)入框架,我使用的是Cocopods來導(dǎo)入框架。畢竟這個(gè)框架手動(dòng)導(dǎo)入起來實(shí)在是太麻煩??

通過stroyboard搭建了一個(gè)登錄界面,并聲明了三個(gè)變量:

@interface ViewController ()
@property (weak, nonatomic) IBOutlet UITextField *userTF;
@property (weak, nonatomic) IBOutlet UITextField *passTF;
@property (weak, nonatomic) IBOutlet UIButton *loginBTN;
@property (nonatomic, strong) LoginService *service; //這是自己寫的模擬后臺(tái)
@end

導(dǎo)入框架后,在view controller中引入ReactiveCocoa的頭文件

#import <ReactiveCocoa/ReactiveCocoa.h> 

在viewdidload中寫上代碼:

RACSignal *userTFSingal = self.userTF.rac_textSignal;
RACSignal *passTFSingal = self.passTF.rac_textSignal;

[userTFSingal subscribeNext:^(id x) {
        NSLog(@"打印出來的用戶名文本:%@",x);
    }];

運(yùn)行之后會(huì)發(fā)現(xiàn) userTF中值發(fā)生了改變就會(huì)打印一次userTF中的text;

也可以通過fliter來設(shè)定subscribeNext響應(yīng)條件。

[[userTFSingal filter:^BOOL(NSString *text) {
        
        return text.length > 3;
    }] subscribeNext:^(id x) {
        NSLog(@"用戶名超過三位的文本:%@",x);
    }];

map操作通過block改變了事件的數(shù)據(jù)。map從上一個(gè)next事件接收數(shù)據(jù),通過執(zhí)行block把返回值傳給下一個(gè)next事件。在上面的代碼中,map以NSString為輸入,取字符串的長度,返回一個(gè)NSNumber。

    [[[userTFSingal map:^id(NSString *text) {
        return @(text.length);
    }]
    filter:^BOOL(NSNumber *length) {
        return [length intValue]>5;
    }]
    subscribeNext:^(id x) {
        NSLog(@"用戶名文本長度:%@",x);
    }];

上面說明了ReactiveCocoa的UITextField部分使用。下面開始實(shí)現(xiàn)登錄邏輯吧!

設(shè)置有效文本長度


- (BOOL)isValidUsername:(NSString *)username {
    return username.length > 3;
}

- (BOOL)isValidPassword:(NSString *)password {
    return password.length > 3;
}

創(chuàng)建有效狀態(tài)信號(hào)

    RACSignal *validUsernameSignal = [self.userTF.rac_textSignal 
map:^id(NSString *text) {
        return @([self isValidUsername:text]);
    }];
    RACSignal *validPasswordSignal = [self.passTF.rac_textSignal
     map:^id(NSString *text) {
         return @([self isValidPassword:text]);
     }];

根據(jù)文本是否有效改變文本框顏色

    [[validPasswordSignal map:^id(NSNumber *passValid) {
        return [passValid boolValue] ? [UIColor clearColor] : [UIColor grayColor];
    }]
     subscribeNext:^(UIColor *color) {
        self.passTF.backgroundColor = color;
    }];

相對(duì)于上面這個(gè)方法更推薦使用下面這個(gè)方法:


RAC(self.userTF, backgroundColor) = [validUsernameSignal map:^id(NSNumber *userValid) {
        return [userValid boolValue] ? [UIColor clearColor] : [UIColor purpleColor];
    }];

    
    RAC(self.passTF, backgroundColor) = [validPasswordSignal map:^id(NSNumber *passValid) {
        return [passValid boolValue] ? [UIColor clearColor] : [UIColor purpleColor];
    }];

聚合信號(hào)
combineLatest:reduce:方法把validUsernameSignal和validPasswordSignal產(chǎn)生的最新的值聚合在一起,并生成一個(gè)新的信號(hào)。每次這兩個(gè)源信號(hào)的任何一個(gè)產(chǎn)生新值時(shí),reduce block都會(huì)執(zhí)行,block的返回值會(huì)發(fā)給下一個(gè)信號(hào)。


RACSignal *loginSignal = [RACSignal combineLatest:@[validPasswordSignal,validUsernameSignal] reduce:^id(NSNumber *usernameValid, NSNumber *passwordValid){
        return @([usernameValid boolValue] && [passwordValid boolValue]);
    }];

這里是模擬后臺(tái)

LoginService接口


typedef void (^RWSignInResponse)(BOOL);

@interface LoginService : NSObject

- (void)signInWithUsername:(NSString *)username password:(NSString *)password complete:(RWSignInResponse)completeBlock;

@end

實(shí)現(xiàn)


- (void)signInWithUsername:(NSString *)username password:(NSString *)password complete:(RWSignInResponse)completeBlock {
    
    double delayInSeconds = 2.0;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        BOOL success = [username isEqualToString:@"user"] && [password isEqualToString:@"password"];
        completeBlock(success);
    });
}

創(chuàng)建登錄信號(hào)

- (RACSignal *)loginInSignal {
    
    return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [self.service signInWithUsername:self.userTF.text password:self.passTF.text complete:^(BOOL success) {
            [subscriber sendNext:@(success)];
            [subscriber sendCompleted];
        }];
        return nil;
    }];
}

UIButton

設(shè)置按鈕狀態(tài)

[loginSignal subscribeNext:^(NSNumber *login) {
        self.loginBTN.enabled = [login boolValue];
    }];

UIButton部分使用

[[self.loginBTN rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
        NSLog(@"登錄按鈕被點(diǎn)擊了");
    }];

輸出登錄結(jié)果

    [[[self.loginBTN rac_signalForControlEvents:UIControlEventTouchUpInside] map:^id(id value) {
        return [self loginInSignal];
    }] subscribeNext:^(id x) {
        NSLog(@"登錄結(jié)果:%@",x);
    }];

這里你會(huì)發(fā)現(xiàn)打印的登錄結(jié)果 不是bool類型
當(dāng)點(diǎn)擊按鈕時(shí),rac_signalForControlEvents發(fā)送了一個(gè)next事件(事件的data是UIButton)。map操作創(chuàng)建并返回了登錄信號(hào),這意味著后續(xù)步驟都會(huì)收到一個(gè)RACSignal。這就是你在subscribeNext:這步看到的。

上面問題的解決方法,有時(shí)候叫做信號(hào)中的信號(hào),換句話說就是一個(gè)外部信號(hào)里面還有一個(gè)內(nèi)部信號(hào)。你可以在外部信號(hào)的subscribeNext:block里訂閱內(nèi)部信號(hào)。不過這樣嵌套太混亂啦,還好ReactiveCocoa已經(jīng)解決了這個(gè)問題。

信號(hào)中的信號(hào)
解決的方法很簡單,只需要把map操作改成flattenMap就可以了:

        [[[self.loginBTN rac_signalForControlEvents:UIControlEventTouchUpInside] flattenMap:^id(id value) {
            return [self loginInSignal];
        }] subscribeNext:^(NSNumber *loginIn) {
            NSLog(@"登錄結(jié)果:%@",loginIn);
            BOOL success = [loginIn boolValue];
            if (success) {
                [self performSegueWithIdentifier:@"Kitten" sender:self];
            }
        }];

為了防止多次點(diǎn)擊Button,使用doNext添加附加操作。

    [[[[self.loginBTN rac_signalForControlEvents:UIControlEventTouchUpInside] doNext:^(id x) {
        self.loginBTN.enabled = NO;
    }] flattenMap:^RACStream *(id value) {
        return [self loginInSignal];
    }] subscribeNext:^(NSNumber *loginIn) {
        NSLog(@"登錄結(jié)果:%@",loginIn);
                    BOOL success = [loginIn boolValue];
                    if (success) {
                        [self performSegueWithIdentifier:@"Kitten" sender:self];
                    }

    }];

好了 登錄demo完成了。
參考文章:
最快讓你上手ReactiveCocoa之基礎(chǔ)篇
ReactiveCocoa Tutorial – The Definitive Introduction: Part 1/2 ==>此文的初始工程
以及譯文

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容