美文网首页
RxSwift官方实例四(计算器)

RxSwift官方实例四(计算器)

作者: 酒茶白开水 | 来源:发表于2020-08-10 11:42 被阅读0次

    代码下载

    搭建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上显示,并清理资源

    相关文章

      网友评论

          本文标题:RxSwift官方实例四(计算器)

          本文链接:https://www.haomeiwen.com/subject/eioddktx.html