You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI计算器无法计算求助:实现运算与等号求值功能

Fixing Your SwiftUI Calculator's Calculation Logic

Hey there! Let's get your SwiftUI calculator actually crunching numbers instead of just showing buttons. The main issue right now is that your button tap handler only handles digit input—none of the operation, equals, or advanced function logic is hooked up. Let's fix that with targeted updates to your code:

Step 1: Enhance the DisplayingActions View Model

First, we need to expand your view model to track selected operations, store the first operand, and handle core calculations. We'll add error handling for edge cases like division by zero and invalid factorial inputs too:

class DisplayingActions: ObservableObject {
    @Published var display = "0"
    @Published var userIsTyping = false
    var storedNumber: Double?
    // Track the selected math operation
    var selectedOperation: CalcButtons?
    
    var displayValue: Double {
        get {
            // Safely convert display string to Double (avoid force unwrap crashes)
            return Double(display) ?? 0
        }
        set {
            // Clean up display: show integers as whole numbers, decimals as-is
            display = newValue.truncatingRemainder(dividingBy: 1) == 0 
                ? String(Int(newValue)) 
                : String(newValue)
        }
    }
    
    // Core calculation logic
    func compute() {
        guard let stored = storedNumber, let operation = selectedOperation else { return }
        var result = 0.0
        
        switch operation {
        case .plus:
            result = stored + displayValue
        case .minus:
            result = stored - displayValue
        case .multiply:
            result = stored * displayValue
        case .divide:
            if displayValue != 0 {
                result = stored / displayValue
            } else {
                display = "Error"
                userIsTyping = false
                return
            }
        case .rootExtraction:
            result = sqrt(stored)
        case .factorial:
            result = factorial(of: stored)
        case .percent:
            result = stored * (displayValue / 100)
        default:
            break
        }
        
        displayValue = result
        storedNumber = nil
        selectedOperation = nil
        userIsTyping = false
    }
    
    // Helper for factorial calculation
    private func factorial(of number: Double) -> Double {
        guard number >= 0, number.truncatingRemainder(dividingBy: 1) == 0 else {
            display = "Error"
            userIsTyping = false
            return 0
        }
        let intNumber = Int(number)
        return (1...intNumber).reduce(1, *)
    }
    
    // Reset calculator to initial state
    func reset() {
        display = "0"
        userIsTyping = false
        storedNumber = nil
        selectedOperation = nil
    }
    
    // Delete last digit from display
    func deleteDigit() {
        if display.count > 1 {
            display.removeLast()
        } else {
            display = "0"
            userIsTyping = false
        }
    }
}

Step 2: Update Button Tap Handling

Next, we'll rewrite the button tap logic in CalculatorButtonsView to handle different button types (digits, operations, equals, etc.) appropriately. This replaces the generic digit-only handler you had before:

struct CalculatorButtonsView: View {
    var button: CalcButtons
    @EnvironmentObject var actions: DisplayingActions
    
    var body: some View{
        Button(action: handleButtonTap) {
            Text(button.title)
                .font(.system(size: 30))
                .frame(width: buttonWidth(button: button), height: (UIScreen.main.bounds.width - 5 * 3 ) / 5)
                .foregroundColor(.white)
                .background(button.backgroundButtonsColor)
                .cornerRadius(buttonWidth(button: button))
        }
    }
    
    private func handleButtonTap() {
        switch button {
        // Handle digit and decimal input
        case .zero, .one, .two, .three, .four, .five, .six, .seven, .eight, .nine, .decimal:
            handleDigitInput()
        // Handle math operations
        case .plus, .minus, .multiply, .divide, .rootExtraction, .factorial, .percent:
            handleOperationInput()
        // Trigger calculation
        case .equals:
            actions.compute()
        // Reset calculator
        case .ac:
            actions.reset()
        // Toggle positive/negative value
        case .minusPlus:
            actions.displayValue *= -1
        // Delete last digit
        case .deleteOneDigit:
            actions.deleteDigit()
        // Handle 10^a operation
        case .tenInExponentiation:
            actions.displayValue = pow(10, actions.displayValue)
            actions.userIsTyping = false
        // Handle X^10 operation
        case .numberInExponentiation:
            actions.displayValue = pow(actions.displayValue, 10)
            actions.userIsTyping = false
        }
    }
    
    private func handleDigitInput() {
        if actions.userIsTyping {
            // Prevent multiple decimals
            if button == .decimal, actions.display.contains(".") {
                return
            }
            actions.display += button.title
        } else {
            // Start with 0. if decimal is tapped first
            actions.display = button == .decimal ? "0." : button.title
            actions.userIsTyping = true
        }
    }
    
    private func handleOperationInput() {
        // Calculate prior operation if one exists
        if actions.storedNumber != nil, actions.selectedOperation != nil {
            actions.compute()
        }
        // Store current value and selected operation
        actions.storedNumber = actions.displayValue
        actions.selectedOperation = button
        actions.userIsTyping = false
        
        // Execute single-operand operations immediately (sqrt, factorial)
        if button == .rootExtraction || button == .factorial {
            actions.compute()
        }
    }
    
    private func buttonWidth(button: CalcButtons) -> CGFloat {
        if button == .zero {
            return (UIScreen.main.bounds.width - 4 * 3 ) / 4 * 1.6
        }
        return (UIScreen.main.bounds.width - 5 * 3 ) / 5
    }
}

Key Improvements Explained

  • Separate Logic by Button Type: Each button category (digits, operations, controls) has dedicated handling, making the code easier to maintain.
  • Error Handling: We added checks for division by zero and non-integer factorial inputs, showing an "Error" message instead of crashing.
  • Clean Display: Integers are shown without trailing decimals, and decimal input is restricted to one per number.
  • Full Feature Support: All your defined buttons (AC, delete, sqrt, factorial, exponents) now work as intended.
  • Chained Calculations: You can now do sequences like 5 + 3 * 2 and get the correct result (since we compute prior operations when a new one is selected).

Try testing these flows to verify:

  1. Basic arithmetic: 5 + 3 = → 8
  2. Single-operand functions: 4 √x → 2; 5 X! → 120
  3. Edge cases: 10 / 0 → "Error"

内容的提问来源于stack exchange,提问作者Brian Anderson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 15:42:51