Быстрый калькулятор - невозможно ввести ноль после десятичной дроби - PullRequest
0 голосов
/ 06 апреля 2020

Я изучаю код калькулятора Swift, и все работает нормально, за исключением того, что я не могу ввести ноль после десятичного знака (например, 12,001 или 1,301) и не могу найти решение. Я уже попробовал несколько вещей, и, к сожалению, я не могу найти вопрос, решающий эту проблему.

Большое спасибо за вашу помощь!

Вот основные части кода.

private var total: Double = 0                   
private var temp: Double = 0                    
private var operating = false                   
private var decimal = false                     
private var operation: OperationType = .none    

private let kDecimalSeparator = Locale.current.decimalSeparator!
private let kMaxLength = 11
private let kTotal = "total"

private enum OperationType {
    case none, addition, substraction, multiplication, division, percent
}

// Format
private let auxFormatter: NumberFormatter = {
    let formatter = NumberFormatter()
    let locale = Locale.current
    formatter.decimalSeparator = locale.decimalSeparator
    formatter.numberStyle = .decimal
    formatter.maximumIntegerDigits = 100
    formatter.minimumFractionDigits = 0
    formatter.maximumFractionDigits = 100
    return formatter
}()

// Format result
private let auxTotalFormatter: NumberFormatter = {
    let formatter = NumberFormatter()
    let locale = Locale.current
    formatter.decimalSeparator = locale.decimalSeparator
    formatter.numberStyle = .decimal
    formatter.maximumIntegerDigits = 100
    formatter.minimumFractionDigits = 0
    formatter.maximumFractionDigits = 100
    return formatter
}()

// Default screen format
private let printFormatter: NumberFormatter = {
    let formatter = NumberFormatter()
    let locale = Locale.current
    formatter.decimalSeparator = locale.decimalSeparator
    formatter.numberStyle = .decimal
    formatter.maximumIntegerDigits = 9
    formatter.minimumFractionDigits = 0
    formatter.maximumFractionDigits = 8
    return formatter
}()




@IBAction func numberDecimalAction(_ sender: UIButton) {

    let currentTemp = auxTotalFormatter.string(from: NSNumber(value: temp))!
    if resultLabel.text?.contains(kDecimalSeparator) ?? false || (!operating && currentTemp.count >= kMaxLength) {
        return
    }

    resultLabel.text = resultLabel.text! + kDecimalSeparator
    decimal = true

    selectVisualOperation()

    sender.shine()
}


@IBAction func numberAction(_ sender: UIButton) {

    operatorAC.setTitle("C", for: .normal)

    var currentTemp = auxTotalFormatter.string(from: NSNumber(value: temp))!
    if !operating && currentTemp.count >= kMaxLength {
        return
    }

    currentTemp = auxFormatter.string(from: NSNumber(value: temp))!

    // After selecting an operation
    if operating {
        total = total == 0 ? temp : total
        resultLabel.text = ""
        currentTemp = ""
        operating = false
    }

    // After selecting decimal
    if decimal {
        currentTemp = "\(currentTemp)\(kDecimalSeparator)"
        decimal = false
    }

    if resultLabel.text?.contains(kDecimalSeparator) ?? true {

        let number = String(sender.tag-1)
        let currentTemp1 = currentTemp.replacingOccurrences(of: ".", with: "", options: .literal, range: nil)
        let currentTemp2 = currentTemp1.replacingOccurrences(of: ",", with: ".", options: .literal, range: nil)
        temp = Double(currentTemp2 + String(number))!

    resultLabel.text = printFormatter.string(from: NSNumber(value: temp))

    selectVisualOperation()

    sender.shine()
        }

    else {
        let number = String(sender.tag-1)
        temp = Double(currentTemp.replacingOccurrences(of: ".", with: "", options: .literal, range: nil) + String(number))!

        resultLabel.text = printFormatter.string(from: NSNumber(value: temp))

        selectVisualOperation()

        sender.shine()
    }
}

// Clear
private func clear() {
    if operation == .none {
        total = 0
    }
    operation = .none
    operatorAC.setTitle("AC", for: .normal)
    if temp != 0 {
        temp = 0
        resultLabel.text = "0"
    } else {
        total = 0
        result()
    }
}

`

1 Ответ

0 голосов
/ 06 апреля 2020

Давайте предположим, что вы выполняете традиционный ввод в стиле калькулятора с кнопками для цифр, кнопкой десятичного разделителя и кнопкой очистки. Проблема с вашим алгоритмом состоит в том, что NumberFormatter с minimumFractionalDigits, равным нулю, сбрасывает завершающие цифры. Поэтому, если вы попытаетесь ввести «1.000», он скажет: «Хорошо, значение равно 1, поэтому строковое представление этого с цифрами нулевой дроби равно« 1 »». Результатом этого является то, что конечные нули никогда не появятся в результирующей строке.

Один из подходов состоит в том, чтобы настроить minimumFractionalDigits на основе того, сколько дробных цифр уже введено. Чтобы облегчить это, вы должны быть в состоянии отслеживать, как были введены дробные цифры.

Есть несколько способов сделать это. Одним из способов является «управление состоянием» (например, иметь свойства для отслеживания того, был ли уже введен десятичный знак и сколько дробных цифр было введено до настоящего времени и т. Д. c.). Чтобы было проще, я просто собираюсь вычислить это из необработанного ввода строки пользователя:

class ViewController: UIViewController {
    let decimalSeparator = Locale.current.decimalSeparator ?? "."

    /// The number formatter
    ///
    /// Note, we don't need to set the decimal separator, as it defaults to the current separator.

    let formatter: NumberFormatter = {
        let formatter = NumberFormatter()
        formatter.numberStyle = .decimal
        return formatter
    }()

    /// The label containing the formatted number

    @IBOutlet weak var resultLabel: UILabel!

    /// This is the user's raw input, just digits and 0 or one decimal separator, not the formatted number in the label

    private var input: String = ""

    /// Just add keystroke to `input` string and then format the label.

    @IBAction func didTapDigit(_ button: UIButton) {
        let digit = ...                                  // determine the numeric value associated with the button that the user tapped; personally I wouldn’t use the `tag` number, but I don’t want to drag us down a tangent, so just do this however you want
        addCharacterToInput("\(digit)")
        updateLabel()
    }

    @IBAction func didTapClear(_ sender: UIButton) {
        resetInput()
        resultLabel.text = "0"
    }

    /// Only add decimal separator if there's not one there already.

    @IBAction func didTapDecimal(_ sender: UIButton) {
        if !hasDecimalSeparator() {
            addCharacterToInput(".")
        }
        updateLabel()
    }
}

private extension ViewController {
    func addCharacterToInput(_ string: String) {
        input += String(string)
    }

    func resetInput() {
        input = ""
    }

    /// How many decimal places in user input.
    ///
    /// - Returns: Returns `nil` if no decimal separator has been entered yet. Otherwise it returns the number of characters after the decimal separator.

    func decimalPlaces() -> Int? {
        guard let range = input.range(of: decimalSeparator) else {
            return nil
        }

        return input.distance(from: range.upperBound, to: input.endIndex)
    }

    /// Does the user input include a decimal separator?
    /// - Returns: Returns `true` if decimal separator present. Returns `false` if not.

    func hasDecimalSeparator() -> Bool {
        input.contains(decimalSeparator)
    }

    /// Update the label on the basis of the `input` string of the raw user input.

    func updateLabel() {
        let fractionalDigits = decimalPlaces()                   // figure out decimal places from `input` string

        formatter.minimumFractionDigits = fractionalDigits ?? 0  // set formatter accordingly

        guard
            let value = Double(input),                           // safely get value from user input ...
            var string = formatter.string(for: value)            // ...and build base string from that.
        else {
            resultLabel.text = "Error"
            return
        }

        if fractionalDigits == 0 {                               // Note, if not `nil` and is zero, that means the user hit decimal separator but has entered no digits yet; we need to manually add decimal separator in output in this scenario
            string += decimalSeparator
        }
        resultLabel.text = string
    }
}

Это дает:

enter image description here

...