【iOS】RxSwift官方Example5--計(jì)算器【轉(zhuǎn)】

原文地址

https://beeth0ven.github.io/RxSwift-Chinese-Documentation/content/more_demo/calculator.html

前言

本來這一篇是想自己寫的,但是看完這個例子后,一臉懵逼,只好去搜搜有沒有人分析這篇例子。結(jié)果還真給我搜索到了,看完后,發(fā)現(xiàn)這篇播客寫的非常詳細(xì),推薦學(xué)Rxswift的都去看看。

簡介

還是先來直接看演示的例子吧。


計(jì)算器

功能就不介紹了。這個的計(jì)算器是RxFeedback架構(gòu),實(shí)際上,這個RxFeedback架構(gòu),我看的還是云里霧里的,還是無法理解。

整體分析

整體結(jié)構(gòu)

圖來自轉(zhuǎn)載出,侵刪


整體結(jié)構(gòu)

從上圖可以看到,我們點(diǎn)擊的按鈕,會先合成命令,然后根據(jù)輸入的命令,決定了計(jì)算器的狀態(tài),最后根據(jù)計(jì)算器的狀態(tài),做出對應(yīng)的操作,也就是上圖的“計(jì)算符”和“屏顯”

合成命令

顯然,我們的命令是通過點(diǎn)擊按鈕產(chǎn)生的,由于這里有許多按鈕,因此我們需要借助Observable.merge方法。

let commands: Observable<CalculatorCommand> = Observable.merge([
            allClearButton.rx.tap.map { _ in .clear},
            changeSignButton.rx.tap.map { _ in .changeSign},
            percentButton.rx.tap.map { _ in .percent},
            
            divideButton.rx.tap.map { _ in .operation(.division)},
            multiplyButton.rx.tap.map { _ in .operation(.multiplication)},
            minusButton.rx.tap.map { _ in .operation(.substraction)},
            plusButton.rx.tap.map { _ in .operation(.addition)},
            
            equalButton.rx.tap.map { _ in .equal},
            
            dotButton.rx.tap.map { _ in .addDot},
            
            zeroButton.rx.tap.map { _ in .addNumber("0")},
            oneButton.rx.tap.map { _ in .addNumber("1")},
            twoButton.rx.tap.map { _ in .addNumber("2")},
            threeButton.rx.tap.map { _ in .addNumber("3")},
            fourButton.rx.tap.map { _ in .addNumber("4")},
            fiveButton.rx.tap.map { _ in .addNumber("5")},
            sixButton.rx.tap.map { _ in .addNumber("6")},
            sevenButton.rx.tap.map { _ in .addNumber("7")},
            eightButton.rx.tap.map { _ in .addNumber("8")},
            nineButton.rx.tap.map { _ in .addNumber("9")}
        ])

通過使用 map 方法將按鈕點(diǎn)擊事件轉(zhuǎn)換為對應(yīng)的命令。如:
將 allClearButton
點(diǎn)擊事件轉(zhuǎn)換為清除命令,將 plusButton
點(diǎn)擊事件轉(zhuǎn)換為相加命令,將 oneButton
點(diǎn)擊事件轉(zhuǎn)換為添加數(shù)字1命令。最后使用 merge 操作符將這些命令合并。于是就得到了我們所需要的命令序列

命令 -> 狀態(tài)之間的轉(zhuǎn)換

幾乎每個頁面都是有狀態(tài)的。我們通過命令序列來對狀態(tài)進(jìn)行修改,然后產(chǎn)生一個新的狀態(tài)。例如,剛進(jìn)頁面后,點(diǎn)擊了按鈕 1 。那么初始狀態(tài)為 0,在執(zhí)行添加數(shù)字1命令后,狀態(tài)就更新為 1。通過這種變換方式,就可以生成一個狀態(tài)序列:

命令 -> 狀態(tài)之間的轉(zhuǎn)換
let system = Observable.system(
            CalculatorState.initial,
            accumulator: CalculatorState.reduce,
            scheduler: MainScheduler.instance,
            feedback: { _ in commands }
            )
            .debug("calculator state")
            .shareReplayLatestWhileConnected()

根據(jù)狀態(tài)顯示

由命令序列觸發(fā),對頁面狀態(tài)進(jìn)行更新,在用更新后的狀態(tài)組成一個序列。這就是我們所需要的狀態(tài)序列。接下來我們用這個狀態(tài)序列來控制頁面顯示

根據(jù)狀態(tài)顯示
system.map { $0.screen }
        .bind(to: resultLabel.rx.text)
        .addDisposableTo(disposeBag)
        
        system.map { $0.sign }
        .bind(to: lastSignLabel.rx.text)
        .addDisposableTo(disposeBag)

state.screen 來控制 resultLabel的顯示內(nèi)容。用 state.sign 來控制 lastSignLabel 的顯示內(nèi)容。

Calculator

控制器主要負(fù)責(zé)數(shù)據(jù)綁定,而整個計(jì)算器的大腦在 Calculator.swift 文件內(nèi)。

State:

這個頁面主要有三種狀態(tài):

enum CalculatorState {
    case oneOperand(screen: String)
    case oneOperandAndOperator(operand: Double, operator: Operator)
    case twoOperandAndOperator(operand: Double, operator: Operator, screen: String)
}
  • oneOperand 一個操作數(shù),例如:進(jìn)入頁面后,輸入 1 時的狀態(tài)
  • oneOperandAndOperator 一個操作數(shù)和一個運(yùn)算符,例如:進(jìn)入頁面后,輸入 1 + 時的狀態(tài)
  • twoOperandsAndOperator 兩個操作數(shù)和一個運(yùn)算符,例如:進(jìn)入頁面后,輸入 1 + 2 時的狀態(tài)

Command:

一共有7個指令:

enum Operator {
    case addition
    case subtraction
    case multiplication
    case division
}

enum CalculatorCommand {
    case clear
    case changeSign
    case percent
    case operation(Operator)
    case equal
    case addNumber(Character)
    case addDot
}
  • clear 清除,重置
  • changeSign 改變正負(fù)號
  • percent 百分比
  • operation 四則運(yùn)算
  • equal 等于
  • addNumber 輸入數(shù)字
  • addDot 輸入 “.”

reduce

當(dāng)命令產(chǎn)生時,將它應(yīng)用到當(dāng)前狀態(tài)上,然后生成新的狀態(tài):

輸入命令后的狀態(tài)轉(zhuǎn)換
extension CalculatorState {
    static func reduce(state: CalculatorState, _ x: CalculatorCommand) -> CalculatorState {
        switch x {
        case .clear:
            return CalculatorState.initial
        case .addNumber(let c):
            return state.mapScreen(transform: { (str) -> String in
                return str == "0" ? String(c) : str + String(c)
            })
        case .addDot:
            return state.mapScreen {
                $0.range(of: ".") == nil ? $0 + "." : $0
            }
        case .changeSign:
            return state.mapScreen {
                "\(-(Double($0) ?? 0.0))"
            }
        case .percent:
            return state.mapScreen {
                 "\((Double($0) ?? 0.0) / 100.0)"
            }
        case .operation(let o):
            switch state {
            case let .oneOperand(screen):
                // 如果只有一個操作數(shù),就添加操作符
                return .oneOperandAndOperator(operand: screen.doubleValue, operator: o)
                // 如果有一個操作數(shù)和操作符,就替換操作符
            case let .oneOperandAndOperator(operand, _):
                return .oneOperandAndOperator(operand: operand, operator: o)
                // 如果有兩個操作數(shù)和一個操作符,將他們的計(jì)算結(jié)果作為操作數(shù)保留,然后加入新的操作符,以及一個操作數(shù) 0.
            case let .twoOperandAndOperator(operand, oldOperator, screen):
                return .twoOperandAndOperator(operand: oldOperator.perform(operand, screen.doubleValue), operator: o, screen: "0")
            }
        case .equal:
            switch state {
                //如果當(dāng)前有兩個操作數(shù)和一個操作符,將他們的計(jì)算結(jié)果作為操作數(shù)保留。否則什么都不做。
                case let .twoOperandAndOperator(operand, opeart, screen):
                let result = opeart.perform(operand, screen.doubleValue)
                return .oneOperand(screen: String(result))
            default:
                return state
            }
        }
    }
}
  • clear 重置當(dāng)前狀態(tài)
  • addNumber, addDot, changeSign, percent 只需要更改屏顯即可
  • operation 需要根據(jù)當(dāng)前狀態(tài)來確定如何變化狀態(tài)。
  • 如果只有一個操作數(shù),就添加操作符。
  • 如果有一個操作數(shù)和操作符,就替換操作符。
  • 如果有兩個操作數(shù)和一個操作符,將他們的計(jì)算結(jié)果作為操作數(shù)保留,然后加入新的操作符,以及一個操作數(shù) 0.
  • equal 如果當(dāng)前有兩個操作數(shù)和一個操作符,將他們的計(jì)算結(jié)果作為操作數(shù)保留。否則什么都不做。

總結(jié)

這篇的核心架構(gòu)是RxFeedback,反正我是不太能理解,不打算深入了解了。

Demo地址

https://github.com/maple1994/RxSwfitTest

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,963評論 6 542
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,348評論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,083評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,706評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,442評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,802評論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,795評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,983評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,542評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,287評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,486評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,030評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,710評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,116評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,412評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,224評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,462評論 2 378

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,824評論 18 139
  • Ubuntu的發(fā)音 Ubuntu,源于非洲祖魯人和科薩人的語言,發(fā)作 oo-boon-too 的音。了解發(fā)音是有意...
    螢火蟲de夢閱讀 99,449評論 9 467
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,718評論 25 708
  • 我是一個愛看小說的人,這樣子媒體人的文章我還是第一次看。感覺腦容量不夠用。看到文章講到醫(yī)生這個章節(jié)時,內(nèi)心感觸還是...
    小草的常閱書院閱讀 372評論 0 2