RxSwift官方实例四(计算器)

代码下载

搭建UI

搭建如下图UI:
UI搭建

实现

计算器逻辑

定义Operator枚举表示基本运算符,如下所示:

/// 基本运算符
enum Operator {
    case addition // 加
    case subtruction // 减
    case multiplication // 乘
    case division // 除
}

extension Operator {
    /// 符号字符串
    var sign: String {
        switch self {
        case .addition:
            return "+"
        case .subtruction:
            return "-"
        case .multiplication:
            return "x"
        case .division:
            return "/"
        }
    }
    
    /// 执行运算
    var perform: (Double, Double) -> Double {
        switch self {
        case .addition:
            return (+)
        case .subtruction:
            return (-)
        case .multiplication:
            return (*)
        case .division:
            return (/)
        }
    }
}

定义CalculatorCommand枚举表示计算器上的所有命令,如下图所示:

/// 计算器命令
enum CalculatorCommand {
    case clear // 输入清除号
    case changeSign // 输入变换符号
    case percent // 输入百分号
    case operation(Operator) // 输入基本运算符
    case equal // 输入等号
    case addNumber(Character) // 输入数字
    case addDoc // 输入小数点
}

定义CalculatorState枚举表示计算器状态:

/// 计算器状态
enum CalculatorState {
    case oneOperand(screen: String) // 一个操作数
    case oneOperandAndOperator(operand: Double, operator: Operator) // 一个操作数和一个操作符
    case twoOperandAndOperator(operand: Double, operator: Operator, screen: String) // 两个个操作数和一个操作符
}

为CalculatorState定义一个计算属性screen表示计算结果:

    /// 屏幕显示数据
    var screen: String {
        switch self {
        case let .oneOperand(screen: screen):
            return screen
        case .oneOperandAndOperator(operand: _, operator: _):
            return CalculatorState.initalScreen
        case let .twoOperandAndOperator(operand: _, operator: _, screen: screen):
            return screen
        }
    }

为CalculatorState定义sign计算属性表示将要执行的操作符:

    /// 屏幕显示操作符
    var sign: String {
        switch self {
        case .oneOperand(screen: _):
            return ""
        case let .oneOperandAndOperator(operand: _, operator: o):
            return o.sign
        case let .twoOperandAndOperator(operand: _, operator: o, screen: _):
            return o.sign
        }
    }

扩展String类型,增加一个removedMantisse计算属性用来去掉字符串末尾的无用字符:

extension String {
    var removedMantisse: String {
        if self.contains(".") && (self.last == "0" || self.last == ".") {
            return String(self[..<self.index(before: self.endIndex)]).removedMantisse
        } else {
            return self
        }
    }
}

代码解析:

  1. 字符串包含. 并且 字符串最后一位是. 或者 0,那么去掉字符串最后一个字符
  2. 递归调用去掉小数字符串末尾无效的0.

为CalculatorState定义mapScreen函数做数据转换:

    /// 转换屏幕上的数据
    /// - Parameter transform: 转换闭包
    func mapScreen(transform: (String) -> String) -> CalculatorState {
        switch self {
        case let .oneOperand(screen: screen):
            return .oneOperand(screen: transform(screen))
        case let .oneOperandAndOperator(operand: operand, operator: operat):
            return .twoOperandAndOperator(operand: operand, operator: operat, screen: transform(CalculatorState.initalScreen))
        case let .twoOperandAndOperator(operand: operand, operator: operat, screen: screen):
            return .twoOperandAndOperator(operand: operand, operator: operat, screen: transform(screen))
        }
    }

为CalculatorState定义reduce函数来计算计算器状态、结果:

    func reduce(command: CalculatorCommand) -> CalculatorState {
        switch command {
        case .clear:
            return CalculatorState.inital
        case .changeSign:
            return self.mapScreen { (screen) -> String in
                if screen.count == 0 {
                    return "-"
                } else if screen[screen.startIndex] == "-" {
                    let result = screen[screen.index(after: screen.startIndex)..<screen.endIndex]
                    return String(result)
                } else {
                    return "-" + screen
                }
            }
        case .percent:
            return self.mapScreen { return "\((Double($0) ?? 0.0)/100.0)" }
        case .operation(let o):
            switch self {
            case let .oneOperand(screen: screen):
                return .oneOperandAndOperator(operand: Double(screen) ?? 0.0, operator: o)
            case let .oneOperandAndOperator(operand: operand, operator: _):
                return .oneOperandAndOperator(operand: operand, operator: o)
            case let .twoOperandAndOperator(operand: operand, operator: oo, screen: screen):
                return .twoOperandAndOperator(operand: oo.perform(operand, Double(screen) ?? 0.0), operator: o, screen: CalculatorState.initalScreen)
            }
        case .equal:
            switch self {
            case .oneOperand(screen: _):
                return self
            case let .oneOperandAndOperator(operand: operand, operator: _):
                return .oneOperand(screen: "\(operand)".removedMantisse)
            case let .twoOperandAndOperator(operand: operand, operator: oo, screen: screen):
                return .oneOperand(screen: "\(oo.perform(operand, Double(screen) ?? 0.0))".removedMantisse)
            }
        case let .addNumber(c):
            return self.mapScreen { (screen) -> String in
                if screen == CalculatorState.initalScreen {
                    return String(c)
                } else if screen == "-0" {
                    return "-" + String(c)
                } else {
                    return screen + String(c)
                }
            }
        case .addDoc:
            return self.mapScreen(transform: { $0.contains(".") || $0 == CalculatorState.initalScreen ? $0 : $0 + "." })
        }

绑定计算器

代码如下:

        /// 所有操作的Observable<CalculatorCommand>数组
        let events: [Observable<CalculatorCommand>] = [
            clearButton.rx.tap.map({ CalculatorCommand.clear }),
            changeSignButton.rx.tap.map({ CalculatorCommand.changeSign }),
            percentButton.rx.tap.map({ CalculatorCommand.percent }),
            equalButton.rx.tap.map({ CalculatorCommand.equal }),
            plusButton.rx.tap.map({ CalculatorCommand.operation(Operator.addition) }),
            minusButton.rx.tap.map({ CalculatorCommand.operation(Operator.subtruction) }),
            multiplyButton.rx.tap.map({ CalculatorCommand.operation(Operator.multiplication) }),
            divideButton.rx.tap.map({ CalculatorCommand.operation(Operator.division) }),
            dotButton.rx.tap.map({ CalculatorCommand.addDoc }),
            zeroButton.rx.tap.map({ CalculatorCommand.addNumber("0") }),
            oneButton.rx.tap.map({ CalculatorCommand.addNumber("1") }),
            twoButton.rx.tap.map({ CalculatorCommand.addNumber("2") }),
            threeButton.rx.tap.map({ CalculatorCommand.addNumber("3") }),
            fourButton.rx.tap.map({ CalculatorCommand.addNumber("4") }),
            fiveButton.rx.tap.map({ CalculatorCommand.addNumber("5") }),
            sixButton.rx.tap.map({ CalculatorCommand.addNumber("6") }),
            sevenButton.rx.tap.map({ CalculatorCommand.addNumber("7") }),
            eightButton.rx.tap.map({ CalculatorCommand.addNumber("8") }),
            nineButton.rx.tap.map({ CalculatorCommand.addNumber("9") })
        ]
        
        /// 初始状态
        let initState = CalculatorState.inital
        /// 操作线程
        let scheduler = MainScheduler.instance
        /// 绑定
        Observable.deferred({
            Observable.merge(events)
                .scan(initState) { $0.reduce(command: $1) }
                .subscribeOn(MainScheduler.instance)
                .startWith(initState)
                .observeOn(scheduler)
        }).subscribe(onNext: { (state) in
            self.signLabel.text = state.sign
            self.resultLabel.text = state.screen
        }).disposed(by: bag)

代码分析:

  1. 构建一个计算器操作序列的数组,然后使用merge操作符合并为一个序列
  2. 使用scan扫描序列,传入计算器初始状态累积来自reduce函数的计算结果
  3. 使用startWith设置初始元素,使用observeOn设置线程
  4. 最后将计算器状态signscreen绑定到Label上显示,并清理资源
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值