ReactiveCocoa代碼實(shí)踐之-UI組件的RAC信號(hào)操作
相關(guān)閱讀:
ReactiveCocoa代碼實(shí)踐之-RAC網(wǎng)絡(luò)請(qǐng)求重構(gòu)這一節(jié)是自己對(duì)網(wǎng)絡(luò)層的一些重構(gòu),本節(jié)是自己一些代碼小實(shí)踐做出的一些demo程序,基本涵蓋大多數(shù)UI控件操作。
一.用UISlider實(shí)現(xiàn)調(diào)色板
假設(shè)我們現(xiàn)在做一個(gè)demo,上面有一個(gè)View用來(lái)展示顏色,下面有三個(gè)UISlider滑竿分別控制RGB的色值,隨著不同滑竿的拖動(dòng)上面view的顏色會(huì)隨之改變。 可以先腦補(bǔ)一下不用RAC該怎么寫。 如果使用RAC只需要將三個(gè)信號(hào)包裝起來(lái)用適當(dāng)?shù)牟僮骶湍軐?shí)現(xiàn)。
// 拖線的UI控件 @property (weak, nonatomic) IBOutlet UIView *topView; @property (weak, nonatomic) IBOutlet UISlider *slider; @property (weak, nonatomic) IBOutlet UISlider *slider; @property (weak, nonatomic) IBOutlet UISlider *slider; // viewDidLoad中 // 分別將三個(gè)控件的改變都包成一個(gè)信號(hào)。 RACSignal *s = [[self.slider rac_newValueChannelWithNilValue:@]startWith:@]; RACSignal *s = [[self.slider rac_newValueChannelWithNilValue:@]startWith:@]; RACSignal *s = [[self.slider rac_newValueChannelWithNilValue:@]startWith:@]; RACSignal *threeSignal = [RACSignal combineLatest:@[s,s,s] reduce:^id(NSNumber* value,NSNumber* value,NSNumber* value){ return @[value,value,value]; }]; // 監(jiān)聽(tīng)這個(gè)"合成"后的信號(hào),改變view的顏色 [threeSignal subscribeNext:^(NSArray *arr) { self.topView.backgroundColor = [UIColor colorWithRed:[arr[] doubleValue] green:[arr[] doubleValue] blue:[arr[] doubleValue] alpha:]; }];
上面的startWith:@0需要注意,如果不加這個(gè)初始值那必須在三個(gè)滑竿都動(dòng)一下才能顯示顏色。 上面使用的方法時(shí)UISlider專屬的,也可以用下面的方法寫,這個(gè)是UIControl的方法會(huì)支持更多其他UI控件。
RACSignal *s1 = [[[self.slider1 rac_signalForControlEvents:UIControlEventValueChanged] map:^id(id value) { return @(self.slider1.value); }] startWith:@0];
二.簡(jiǎn)潔代碼實(shí)現(xiàn)登錄邏輯
在UI控件中難點(diǎn)不多,但是值得注意的就是各種狀態(tài)的多級(jí)管理,如果哪里疏忽了就很容易造成bug,這也就導(dǎo)致很多地方有判斷結(jié)構(gòu),并且各種來(lái)回賦值。 假設(shè)現(xiàn)在需要做一個(gè)登錄框,有賬號(hào)密碼和同意條款三項(xiàng),必須滿足賬號(hào)密碼大于2位且選擇了同意,才允許注冊(cè)。 舊的寫法非常麻煩,還需要監(jiān)聽(tīng)valueChange事件等。如果用RAC只需要寫如下代碼:
@property (weak, nonatomic) IBOutlet UITextField *accountTxt; @property (weak, nonatomic) IBOutlet UITextField *pwdTxt; @property (weak, nonatomic) IBOutlet SXSwitch *agreeSw; // 同意條款 @property (weak, nonatomic) IBOutlet UIButton *loginBtn; // 注冊(cè)按鈕 // viewDidLoad方法 self.loginBtn.enabled = NO; RAC(self.loginBtn , enabled) = [RACSignal combineLatest:@[self.accountTxt.rac_textSignal , self.pwdTxt.rac_textSignal, self.agreeSw.rac_newOnChannel] reduce:^(NSString *account, NSString *pwd, NSNumber *isOn){ return @((account.length > )&&(pwd.length >)&&[isOn boolValue]); }];
這其中combineLatest數(shù)組中用的都是控件專屬的信號(hào), 也可以使用RAC(self.agreeSw, on) 這種寫法直接把某一個(gè)屬性的狀態(tài)用信號(hào)傳過(guò)來(lái)。但是這里需要注意:假設(shè)你監(jiān)聽(tīng)了A類的B屬性時(shí),只有走了B屬性的set方法才會(huì)被監(jiān)聽(tīng)捕獲,如果是通過(guò)其他方法修改的屬性值則無(wú)效。 比如UISwitch的來(lái)回?fù)軇?dòng)過(guò)程中并沒(méi)有走on這個(gè)屬性的set方法。
三.通過(guò)interval方法實(shí)現(xiàn)時(shí)鐘
這是一種默認(rèn)循環(huán)的方法,除非你通過(guò)控制Disposable把他禁了。 interval這個(gè)方法就是傳入一個(gè)參數(shù)是間隔時(shí)間,然后內(nèi)部每隔這一段時(shí)間就發(fā)一個(gè)[NSDate date]的對(duì)象,然后block內(nèi)部把這個(gè)date設(shè)置一個(gè)格式以字符串的方法返回。
RAC(self, timeLabel.text) = [[[RACSignal interval: onScheduler:[RACScheduler currentScheduler]] startWith:[NSDate date]] map:^id (NSDate *value) { NSDateComponents *dateComponents = [[NSCalendar currentCalendar] components:NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond fromDate:value]; return [NSString stringWithFormat:@"%ld:%ld:%ld", (long)dateComponents.hour, (long)dateComponents.minute, (long)dateComponents.second]; }];
四.其他控件事件操作
除了上面的UIButton,UISlider,UIControl的分類方法還有很多操作
UISegmentedControl (RACSignalSupport)分類就為此控件提供了便捷處理方法,相比于常規(guī)的監(jiān)聽(tīng)seg的元素點(diǎn)擊事件,再取出當(dāng)前選中的index。RACSignal可以直接得到需要的值
[[self.seg rac_newSelectedSegmentIndexChannelWithNilValue:@]subscribeNext:^(id x) { // 返回的基本數(shù)據(jù)類型都被裝包成NSNumber,可在此做一些判斷操作 NSLog(@"selectIndex-%@",x); }];
UIDatePicker (RACSignalSupport)分類為時(shí)間選擇框封裝了一個(gè)操作,每當(dāng)選框改變時(shí)返回NSDate類型
[[picker rac_newDateChannelWithNilValue:[NSDate date]]subscribeNext:^(NSDate *x) { NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; dateFormatter.dateFormat = @"HH:mm"; dateFormatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"zh_CN"]; NSString *dateStr = [dateFormatter stringFromDate:x]; }];
如果在這里給控件賦值,每一次改動(dòng)都會(huì)讓展示控件的值更新,如果有的設(shè)計(jì)不希望這么頻繁只有在點(diǎn)擊確認(rèn)后再將時(shí)間顯示可以根據(jù)自己喜好自行賦值。
除此這些還有很多UI控件綁定的方法 UIAlertView (RACSignalSupport) 里面就提供了一些方法比如點(diǎn)擊彈窗按鈕可以在subscribeNext里統(tǒng)一處理各個(gè)按鈕的點(diǎn)擊事件。 但是現(xiàn)在UIAlertView已被UIAlertController取代所以,UIAlertView和UIActionSheet這里可以忽略不提。
五.生命周期相關(guān)操作
UITableView和UICollectionView的Cell都有重用的機(jī)制,如果給這個(gè)Cell綁定了一些監(jiān)聽(tīng),那這個(gè)Cell被重用它子控件的監(jiān)聽(tīng)該何去何從?UITableViewCell (RACSignalSupport)、UICollectionReusableView (RACSignalSupport)這兩個(gè)分類里提供了即將重用時(shí)的信號(hào)rac_prepareForReuseSignal
做過(guò)兩個(gè)類似的場(chǎng)景,一個(gè)是tableView的cell回復(fù)按鈕點(diǎn)擊會(huì)跳到回復(fù)頁(yè),一個(gè)是collection的item內(nèi)有個(gè)按鈕點(diǎn)擊就變顏色。
// UITableViewDataSource - (UITableViewCell *)tableView:(nonnull UITableView *)tableView cellForRowAtIndexPath:(nonnull NSIndexPath *)indexPath { SXFeedbackCell * cell = [tableView dequeueReusableCellWithIdentifier:@"SXFeedbackCell"]; @weakify(self) [[[cell.replyButton rac_signalForControlEvents:UIControlEventTouchUpInside] takeUntil:cell.rac_prepareForReuseSignal] subscribeNext:^(UIButton *x) { @strongify(self) // 處理一些其他邏輯 [self.navigationController pushViewController:[SXReplyPage new] animated:YES]; }]; return cell; } // UICollectionViewDataSource - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { UICollectionViewCell * cell = [collectionView dequeueReusableCellWithIdentifier:@"SXDownloadCell"]; [[[cell.changeBtn rac_signalForControlEvents:UIControlEventTouchUpInside] takeUntil:cell.rac_prepareForReuseSignal] subscribeNext:^(UIButton *x) { cell.backgroundColor = [UIColor grayColor]; }]; return cell; }
其中takeUntil操作是監(jiān)聽(tīng)某個(gè)事件直到什么時(shí)候結(jié)束。當(dāng)這個(gè)cell即將重用時(shí)rac_prepareForReuseSignal到來(lái)會(huì)觸發(fā)disposable信號(hào)結(jié)束監(jiān)聽(tīng)。
非重用類型的控件的生命周期可以用rac_willDeallocSignal 信號(hào)監(jiān)聽(tīng),但是在開發(fā)中很少會(huì)用到此信號(hào),因?yàn)榇蠖嗍切盘?hào)操作的內(nèi)部代碼里都幫你做了這個(gè)操作,即監(jiān)聽(tīng)一個(gè)事件直到自己結(jié)束時(shí)停止監(jiān)聽(tīng)。
// rac_textSignal源碼 - (RACSignal *)rac_textSignal { @weakify(self); return [[[[[RACSignal defer:^{ @strongify(self); return [RACSignal return:self]; }] concat:[self rac_signalForControlEvents:UIControlEventAllEditingEvents]] map:^(UITextField *x) { return x.text; }] takeUntil:self.rac_willDeallocSignal] setNameWithFormat:@"%@ -rac_textSignal", self.rac_description]; } // rac_signalForControlEvents源碼 - (RACSignal *)rac_signalForControlEvents:(UIControlEvents)controlEvents { @weakify(self); return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) { @strongify(self); [self addTarget:subscriber action:@selector(sendNext:) forControlEvents:controlEvents]; [self.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ [subscriber sendCompleted]; }]]; return [RACDisposable disposableWithBlock:^{ @strongify(self); [self removeTarget:subscriber action:@selector(sendNext:) forControlEvents:controlEvents]; }]; }] setNameWithFormat:@"%@ -rac_signalForControlEvents: %lx", self.rac_description, (unsigned long)controlEvents]; }
關(guān)于ReactiveCocoa代碼實(shí)踐之-UI組件的RAC信號(hào)操作就給大家介紹這么多,希望對(duì)大家有所幫助!
- react quill中圖片上傳由默認(rèn)轉(zhuǎn)成base64改成上傳到服務(wù)器的方法
- react build 后打包發(fā)布總結(jié)
- 詳解React開發(fā)中使用require.ensure()按需加載ES6組件
- React+Ant Design開發(fā)環(huán)境搭建的實(shí)現(xiàn)步驟
- react+ant design實(shí)現(xiàn)Table的增、刪、改的示例代碼
- React降級(jí)配置及Ant Design配置詳解
- 詳解Ant Design of React的安裝和使用方法
- React使用UI(Ant?Design)框架的詳細(xì)過(guò)程
相關(guān)文章
使用Axios在React中請(qǐng)求數(shù)據(jù)的方法詳解
這篇文章主要給大家介紹了初學(xué)React,如何規(guī)范的在react中請(qǐng)求數(shù)據(jù),主要介紹了使用axios進(jìn)行簡(jiǎn)單的數(shù)據(jù)獲取,加入狀態(tài)變量,優(yōu)化交互體驗(yàn),自定義hook進(jìn)行數(shù)據(jù)獲取和使用useReducer改造請(qǐng)求,本文主要適合于剛接觸React的初學(xué)者以及不知道如何規(guī)范的在React中獲取數(shù)據(jù)的人2023-09-09深入了解響應(yīng)式React Native Echarts組件
近年來(lái),隨著移動(dòng)端對(duì)數(shù)據(jù)可視化的要求越來(lái)越高,通過(guò) WebView 在移動(dòng)端使用 Echarts 這樣功能強(qiáng)大的前端數(shù)據(jù)可視化庫(kù),是解決問(wèn)題的好辦法。下面就和小編來(lái)一起學(xué)習(xí)一下吧2019-05-05React Native 使用Fetch發(fā)送網(wǎng)絡(luò)請(qǐng)求的示例代碼
本篇文章主要介紹了React Native 使用Fetch發(fā)送網(wǎng)絡(luò)請(qǐng)求的示例代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12React中useCallback useMemo到底該怎么用
在React函數(shù)組件中,當(dāng)組件中的props發(fā)生變化時(shí),默認(rèn)情況下整個(gè)組件都會(huì)重新渲染。換句話說(shuō),如果組件中的任何值更新,整個(gè)組件將重新渲染,包括沒(méi)有更改values/props的函數(shù)/組件。在react中,我們可以通過(guò)memo,useMemo以及useCallback來(lái)防止子組件的rerender2023-02-02React中useState的使用方法及注意事項(xiàng)
useState通過(guò)在函數(shù)組件里調(diào)用它來(lái)給組件添加一些內(nèi)部state,下面這篇文章主要給大家介紹了關(guān)于React中useState的使用方法及注意事項(xiàng)的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-08-08React-Hook中使用useEffect清除定時(shí)器的實(shí)現(xiàn)方法
這篇文章主要介紹了React-Hook中useEffect詳解(使用useEffect清除定時(shí)器),主要介紹了useEffect的功能以及使用方法,還有如何使用他清除定時(shí)器,需要的朋友可以參考下2022-11-11