这是一个计算器,是RxSwift官方的示例demo,可在 这里下载
整个项目只有3个文件
我们先看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函数传入的 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映射到计算符和屏显。
这个计算器主要有三种状态:
enum CalculatorState {
case oneOperand(screen: String)
case oneOperandAndOperator(operand: Double, operator: Operator)
case twoOperandsAndOperator(operand: Double, operator: Operator, screen: String)
}
oneOperand 一个操作数,例如: 输入 1 时的状态
oneOperandAndOperator 一个操作数和一个运算符,例如: 输入 1 + 时的状态
twoOperandsAndOperator 两个操作数和一个运算符,例如: 输入 1 + 2 时的状态
计算器提供了七种命令
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
}
}