RxSwift UI交互 - II

對(duì)初始項(xiàng)目的改動(dòng)

為了演示RxSwift的用法靖苇,我們對(duì)上一個(gè)視頻用到的項(xiàng)目徽级,做了以下改動(dòng):

首先,給Sign Up添加了一個(gè)Segue请垛,點(diǎn)擊后,會(huì)切換到一個(gè)用戶提交各種信息的UI洽议,我們所有要演示的交互都在這個(gè)新的UI上進(jìn)行宗收;

image

其次,在這個(gè)新的UI里:

  • UIDatePicker用于設(shè)置生日亚兄、當(dāng)輸入的生日小于當(dāng)天時(shí)混稽,我們會(huì)在這個(gè)picker外圍顯示一個(gè)綠框;
  • Male和Female是兩個(gè)按鈕,它加載了兩個(gè)UIImage用于模擬二選一的效果匈勋。只有這兩個(gè)內(nèi)容正確之后礼旅,我們才啟用底部的update按鈕,否則就禁用它洽洁;
  • “Know swift”是一個(gè)UISwitch痘系,表示用戶是否了解Swift;
  • 下面的UISlider則表示用戶對(duì)Swift的熟悉程度饿自;
  • 接下來(lái)是一個(gè)UIStepper汰翠,用于設(shè)置對(duì)Swift的興趣,當(dāng)點(diǎn)擊加號(hào)時(shí)昭雌,紅心會(huì)變大复唤;反之則變小烛卧;
  • 最后佛纫,submit按鈕只有在所有UI控件都有正確值的時(shí)候才啟用,否則是禁用狀態(tài)总放;
image

第三呈宇,我們?yōu)檫@個(gè)新的UI定義了一個(gè)AboutYouViewController,它里面有我們需要的IBOutlet以及DisposeBag间聊;

class AboutYouViewController: UIViewController {

    @IBOutlet weak var birthday: UIDatePicker!
    @IBOutlet weak var male: UIButton!
    @IBOutlet weak var female: UIButton!
    @IBOutlet weak var knowSwift: UISwitch!
    @IBOutlet weak var swiftLevel: UISlider!
    @IBOutlet weak var passionToLearn: UIStepper!
    @IBOutlet weak var heartHeight: NSLayoutConstraint!
    @IBOutlet weak var update: UIButton!

    var bag: DisposeBag! = DisposeBag()

    // Omit for simplicity…
}

第四攒盈,給InputValidator添加了一個(gè)新的方法,用于驗(yàn)證UIDatePicker輸入的日期是否小于當(dāng)天哎榴;

class func isValidDate(date: NSDate) -> Bool {
    let calendar = NSCalendar.currentCalendar()
    let compare = calendar.compareDate(date,
            toDate: NSDate(),
            toUnitGranularity: .Day)

    return compare == .OrderedAscending
}

最后型豁,我們?cè)?code>InputValidator還添加了一些需要用到的圖片資源;

image

這就是我們對(duì)接下來(lái)內(nèi)容的準(zhǔn)備工作尚蝌,了解清楚之后迎变,我們就可以開(kāi)工了。


校驗(yàn)UIDatePicker

首先來(lái)處理UIDatePicker飘言,給它添加邊框的代碼和UITextField是類似的衣形。

RxSwiftUIDatePicker添加了一個(gè)擴(kuò)展叫做rx_date,我們可以直接把這個(gè)Observable<NSDate>映射成一個(gè)Observable<Bool>表示輸入的生日是否合法姿鸿。

viewDidLoad方法里谆吴,添加下面的代碼:

let birthdayObservable = self.birthday.rx_date.map {
    InputValidator.isValidDate($0)
}

然后,把得到的結(jié)果進(jìn)一步mapUIColor苛预,并且訂閱它:

birthdayObservable.map {
    $0 ? UIColor.greenColor() : UIColor.clearColor()
}.subscribeNext {
    self.birthday.layer.borderColor = $0.CGColor
}.addDisposableTo(self.bag)

最后句狼,別忘記設(shè)置borderWidth屬性:

self.birthday.layer.borderWidth = 1

完成后,Command + R編譯執(zhí)行热某,就可以看到結(jié)果了腻菇,只有在設(shè)置當(dāng)天以前的日期時(shí)胳螟,UIDatePicker才會(huì)有綠色的邊框。

image

理解Rx編程中的Subject

接下來(lái)筹吐,我們來(lái)處理選擇性別的按鈕糖耸。由于默認(rèn)情況下,沒(méi)有任何一個(gè)性別被選中丘薛,因此嘉竟,實(shí)際上我們要處理的邏輯有兩個(gè):

  • 用戶選擇了一個(gè)性別,表示按鈕被點(diǎn)擊了洋侨;
  • 用戶具體選擇的是哪個(gè)性別周拐,我們要根據(jù)這個(gè)選擇加載正確的UIImage

我們來(lái)逐步實(shí)現(xiàn)它凰兑。

首先,添加一個(gè)enum审丘,表示用戶選擇的性別:

enum Gender {
    case notSelected
    case male
    case female
}

其次吏够,我們需要一個(gè)observer,用來(lái)訂閱按鈕的點(diǎn)擊事件滩报,這樣锅知,我們就知道按鈕被點(diǎn)擊了,并以此作為啟用udpate按鈕的依據(jù)之一(這跟我們上個(gè)視頻中用到的例子是相同的)脓钾。

但是售睹,我們還需要這個(gè)observer是一個(gè)Observable,因?yàn)槲覀冇嗛喫裳担⒏鶕?jù)用戶點(diǎn)擊的按鈕設(shè)置按鈕圖片昌妹,怎么做呢?

在Reactive編程里握截,有一個(gè)概念叫做Subject飞崖,它是一類對(duì)象的統(tǒng)稱。這類對(duì)象既可以做Observer谨胞,又可以做Observable固歪。大家可以在Reactive.io找到它的詳細(xì)定義

RxSwift里胯努,我們使用其中一個(gè)叫做Variable的Subject牢裳,簡(jiǎn)單用圖來(lái)表示,它是這樣的:

  • Variable作為Observer叶沛,它可以訂閱一個(gè)Observable蒲讯,我們管這個(gè)Observable叫做source observable
image
  • Variable作為Observable恬汁,它還可以被其他的Observer訂閱伶椿,每當(dāng)有新訂閱的時(shí)候辜伟,它就會(huì)發(fā)送最近一次發(fā)生的事件以及以后陸續(xù)會(huì)發(fā)生的事件;
image
  • 而當(dāng)source observable發(fā)生.Complete.Error時(shí)脊另,Variable會(huì)向observer轉(zhuǎn)發(fā)對(duì)應(yīng)的事件导狡,并自動(dòng)被回收;
image

介紹了理論之后偎痛,我們來(lái)看代碼旱捧。先定義一個(gè)Variable<Gender>

let genderSelection = Variable<Gender>(.notSelected)

讓它先去訂閱按鈕的點(diǎn)擊事件:

self.male.rx_tap.map {
    return Gender.male
}
.bindTo(genderSelection)
.addDisposableTo(self.bag)

self.female.rx_tap.map {
    return Gender.female
}
.bindTo(genderSelection)
.addDisposableTo(self.bag)

這里有兩點(diǎn)需要說(shuō)明:

  1. 我們使用map方法把點(diǎn)擊事件變成了一個(gè)值為Gender enum事件;
  2. 使用bindTo訂閱了map后的事件踩麦,在這里枚赡,bindTosubscribe是等價(jià)的,只是當(dāng)我們想表達(dá)“把一個(gè)值綁定給Variable這樣的語(yǔ)義時(shí)”谓谦,bindTosubscribe的表意更明確一些贫橙;

這樣,當(dāng)不同的按鈕被點(diǎn)擊時(shí)反粥,genderSelection就有不同的值了卢肃。接下來(lái),我們要讓genderSelection是一個(gè)observable才顿,并根據(jù)它的值來(lái)為按鈕設(shè)置圖片:

genderSelection.asObservable().subscribeNext({
    switch $0 {
    case .male:
        self.male.setImage(UIImage(named: "check"), 
            forState: .Normal)
        self.female.setImage(UIImage(named: "uncheck"), 
            forState: .Normal)
    case .female:
        self.male.setImage(UIImage(named: "uncheck"), 
            forState: .Normal)
        self.female.setImage(UIImage(named: "check"), 
            forState: .Normal)
    default:
        break;
    }
}).addDisposableTo(self.bag)

在上面的代碼里莫湘,我們使用genderSelection.asObservable()把一個(gè)Variable明確轉(zhuǎn)換成了observable。這樣郑气,我們就能使用subscribeNext來(lái)訂閱它了幅垮,在上面的代碼里$0的值是我們之前定義的Gender enum,我們只要根據(jù)用戶點(diǎn)擊的選擇尾组,為按鈕設(shè)置正確的圖片就好了忙芒。

最后,還有一個(gè)工作演怎。當(dāng)用戶設(shè)置了生日和性別之后匕争,UI上所有控件的值就都有合法值了,我們添加啟用Submit按鈕的代碼爷耀,這和我們?cè)谏蟼€(gè)視頻中的代碼是類似的甘桑。

我們先把genderSelection變成一個(gè)Observable<Bool>

let genderBtnObservable = genderSelection.asObservable().map {
    return $0 != .notSelected ? true : false
}

然后,使用combineLatest方法歹叮,把birthdayObservablegenderBtnObservable合并起來(lái)跑杭,再變成一個(gè)Observable<Bool>

Observable.combineLatest(birthdayObservable, genderBtnObservable) {
    return [$0, $1]
}.map {
    $0.reduce(true, combine: { $0 && $1 })
}

然后,訂閱這個(gè)合并的結(jié)果咆耿,把它和update按鈕的rx_tap”綁定”起來(lái):

Observable.combineLatest(birthdayObservable, genderBtnObservable) {
    return [$0, $1]
}.map {
    $0.reduce(true, combine: { $0 && $1 })
}
.bindTo(self.update.rx_enabled)
.addDisposableTo(self.bag)

這里有兩點(diǎn)要說(shuō)明:

  1. 我們?cè)僖淮问褂昧?code>bindTo代替了subscribe用于表達(dá)“綁定”的語(yǔ)義德谅;
  2. rx_enabledRxSwiftUIButton添加的另外一個(gè)擴(kuò)展,表示按鈕是否啟用萨螺;

至此窄做,我們就實(shí)現(xiàn)了兩個(gè)功能:

  1. 模擬了二選一的按鈕效果愧驱;
  2. 當(dāng)UI上所有控件都有合法值時(shí),啟用update按鈕椭盏;

Command + R編譯執(zhí)行组砚,我們就能看到對(duì)應(yīng)的效果了:

image

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市掏颊,隨后出現(xiàn)的幾起案子糟红,更是在濱河造成了極大的恐慌,老刑警劉巖乌叶,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件盆偿,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡准浴,警方通過(guò)查閱死者的電腦和手機(jī)事扭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)乐横,“玉大人句旱,你說(shuō)我怎么就攤上這事∥保” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵腥泥,是天一觀的道長(zhǎng)匾南。 經(jīng)常有香客問(wèn)我,道長(zhǎng)蛔外,這世上最難降的妖魔是什么蛆楞? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮夹厌,結(jié)果婚禮上豹爹,老公的妹妹穿的比我還像新娘。我一直安慰自己矛纹,他們只是感情好臂聋,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著或南,像睡著了一般孩等。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上采够,一...
    開(kāi)封第一講書(shū)人閱讀 48,970評(píng)論 1 284
  • 那天肄方,我揣著相機(jī)與錄音,去河邊找鬼蹬癌。 笑死权她,一個(gè)胖子當(dāng)著我的面吹牛虹茶,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播隅要,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼蝴罪,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了拾徙?” 一聲冷哼從身側(cè)響起洲炊,我...
    開(kāi)封第一講書(shū)人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎尼啡,沒(méi)想到半個(gè)月后暂衡,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡崖瞭,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年狂巢,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片书聚。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡唧领,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出雌续,到底是詐尸還是另有隱情斩个,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布驯杜,位于F島的核電站受啥,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏鸽心。R本人自食惡果不足惜滚局,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望顽频。 院中可真熱鬧藤肢,春花似錦、人聲如沸糯景。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蟀淮。三九已至丑孩,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間灭贷,已是汗流浹背温学。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留甚疟,地道東北人仗岖。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓逃延,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親轧拄。 傳聞我的和親對(duì)象是個(gè)殘疾皇子揽祥,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 前言 在之前用Objective-C語(yǔ)言做項(xiàng)目的時(shí)候,我習(xí)慣性的會(huì)利用MVVM模式去架構(gòu)項(xiàng)目檩电,在框架Reactiv...
    Tangentw閱讀 21,161評(píng)論 32 123
  • Observer(觀察者)拄丰、Observable(可觀察序列) ? 核心理解就是一個(gè)觀察者(Observer)訂...
    Harely閱讀 290評(píng)論 0 0
  • 函數(shù)式編程 本文介紹了函數(shù)響應(yīng)式編程(FRP)以及 RxSwift 的一些內(nèi)容, 源自公司內(nèi)部的一次分享. 不變狀...
    icetime17閱讀 1,032評(píng)論 0 0
  • 本章將向你介紹另一個(gè)框架,它是原生RxSwift庫(kù)的一部分:RxCocoa俐末。 RxCocoa全平臺(tái)通用料按。每個(gè)平臺(tái)有...
    大灰很閱讀 858評(píng)論 3 2
  • 內(nèi)容概覽: Rx Marble Diagrams(寶石圖) 關(guān)鍵概念 Event - 事件 Observable ...
    FicowShen閱讀 3,324評(píng)論 0 8