Calculator.gif
這是一個計算器煤惩,是RxSwift官方的示例demo,可在 這里下載
整個項目只有3個文件
image.png
我們先看CalculatorViewController.swift這個文件猾浦,核心代碼如下
override func viewDidLoad() {
typealias FeedbackLoop = (ObservableSchedulerContext<CalculatorState>) -> Observable<CalculatorCommand>
let uiFeedback: FeedbackLoop = bind(self) { this, state in
let subscriptions = [
state.map { $0.screen }.bind(to: this.resultLabel.rx.text),
state.map { $0.sign }.bind(to: this.lastSignLabel.rx.text)
]
let events: [Observable<CalculatorCommand>] = [
this.allClearButton.rx.tap.map { _ in .clear },
this.changeSignButton.rx.tap.map { _ in .changeSign },
this.percentButton.rx.tap.map { _ in .percent },
this.divideButton.rx.tap.map { _ in .operation(.division) },
this.multiplyButton.rx.tap.map { _ in .operation(.multiplication) },
this.minusButton.rx.tap.map { _ in .operation(.subtraction) },
this.plusButton.rx.tap.map { _ in .operation(.addition) },
this.equalButton.rx.tap.map { _ in .equal },
this.dotButton.rx.tap.map { _ in .addDot },
this.zeroButton.rx.tap.map { _ in .addNumber("0") },
this.oneButton.rx.tap.map { _ in .addNumber("1") },
this.twoButton.rx.tap.map { _ in .addNumber("2") },
this.threeButton.rx.tap.map { _ in .addNumber("3") },
this.fourButton.rx.tap.map { _ in .addNumber("4") },
this.fiveButton.rx.tap.map { _ in .addNumber("5") },
this.sixButton.rx.tap.map { _ in .addNumber("6") },
this.sevenButton.rx.tap.map { _ in .addNumber("7") },
this.eightButton.rx.tap.map { _ in .addNumber("8") },
this.nineButton.rx.tap.map { _ in .addNumber("9") }
]
return Bindings(subscriptions: subscriptions, events: events)
}
Observable.system(
initialState: CalculatorState.initial,
reduce: CalculatorState.reduce,
scheduler: MainScheduler.instance,
scheduledFeedback: uiFeedback
)
.subscribe()
.disposed(by: disposeBag)
}
func formatResult(_ result: String) -> String {
if result.hasSuffix(".0") {
return String(result[result.startIndex ..< result.index(result.endIndex, offsetBy: -2)])
} else {
return result
}
}
該項目使用了RxFeedback框架陆错,有時間我再寫一篇分析RxFeedback源碼的文章。 關于RxFeedback的簡介 看這里
bind(self) { this, state in ...
這里閉包里的this即是bind函數(shù)傳入的 self
上面的代碼段 let events: [Observable<CalculatorCommand>] = [ ... ]
表示將按鈕的點擊事件轉換為相應的命令跃巡。
let subscriptions = [
state.map { $0.screen }.bind(to: this.resultLabel.rx.text),
state.map { $0.sign }.bind(to: this.lastSignLabel.rx.text)
]
上面的代碼表示將state映射到計算符和屏顯危号。
image.png
這個計算器主要有三種狀態(tài):
enum CalculatorState {
case oneOperand(screen: String)
case oneOperandAndOperator(operand: Double, operator: Operator)
case twoOperandsAndOperator(operand: Double, operator: Operator, screen: String)
}
oneOperand 一個操作數(shù),例如: 輸入 1 時的狀態(tài)
oneOperandAndOperator 一個操作數(shù)和一個運算符素邪,例如: 輸入 1 + 時的狀態(tài)
twoOperandsAndOperator 兩個操作數(shù)和一個運算符外莲,例如: 輸入 1 + 2 時的狀態(tài)
計算器提供了七種命令
enum CalculatorCommand {
case clear
case changeSign
case percent
case operation(Operator)
case equal
case addNumber(Character)
case addDot
}
extension CalculatorState {
static func reduce(state: CalculatorState, _ x: CalculatorCommand) -> CalculatorState {
switch x {
case .clear:
return CalculatorState.initial
case .addNumber(let c):
return state.mapScreen { return $0 == "0" ? String(c) : $0 + 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):
return .oneOperandAndOperator(operand: screen.doubleValue, operator: o)
case let .oneOperandAndOperator(operand, _):
return .oneOperandAndOperator(operand: operand, operator: o)
case let .twoOperandsAndOperator(operand, oldOperator, screen):
return .twoOperandsAndOperator(operand: oldOperator.perform(operand, screen.doubleValue), operator: o, screen: "0")
}
case .equal:
switch state {
case let .twoOperandsAndOperator(operand, operat, screen):
let result = operat.perform(operand, screen.doubleValue)
return .oneOperand(screen: String(result))
default:
return state
}
}
}
}
一些其他輔助代碼:
extension CalculatorState {
static let initial = CalculatorState.oneOperand(screen: "0")
func mapScreen(transform: (String) -> String) -> CalculatorState {
switch self {
case let .oneOperand(screen):
return .oneOperand(screen: transform(screen))
case let .oneOperandAndOperator(operand, operat):
return .twoOperandsAndOperator(operand: operand, operator: operat, screen: transform("0"))
case let .twoOperandsAndOperator(operand, operat, screen):
return .twoOperandsAndOperator(operand: operand, operator: operat, screen: transform(screen))
}
}
var screen: String {
switch self {
case let .oneOperand(screen):
return screen
case .oneOperandAndOperator:
return "0"
case let .twoOperandsAndOperator(_, _, screen):
return screen
}
}
var sign: String {
switch self {
case .oneOperand:
return ""
case let .oneOperandAndOperator(_, o):
return o.sign
case let .twoOperandsAndOperator(_, o, _):
return o.sign
}
}
}
extension Operator {
var sign: String {
switch self {
case .addition: return "+"
case .subtraction: return "-"
case .multiplication: return "×"
case .division: return "/"
}
}
var perform: (Double, Double) -> Double {
switch self {
case .addition: return (+)
case .subtraction: return (-)
case .multiplication: return (*)
case .division: return (/)
}
}
/*
這段代碼解釋一下,這種寫法我也是第一次見兔朦。對于addition偷线,它返回了一個接受兩個Double值并返回它們之和的閉包,即(+)沽甥。
對于subtraction声邦,它返回了一個接受兩個Double值并返回它們之差的閉包,即(-)摆舟。
對于multiplication亥曹,它返回了一個接受兩個Double值并返回它們乘積的閉包邓了,即(*)。
對于division媳瞪,它返回了一個接受兩個Double值并返回它們商的閉包骗炉,即(/)。
*/
}
private extension String {
var doubleValue: Double {
guard let double = Double(self) else {
return Double.infinity
}
return double
}
}