Попытка добавить процент к вычислению 2 UITextFields - PullRequest
0 голосов
/ 11 мая 2019

Я использую Swift 5, и в 60 лет я относительно новичок, и самообучаться не так-то просто, чтобы получить ответы, но здесь - у меня есть 2 текстовых поля, одно значение уже передано из другого контроллера представления, затем у меня есть стоимость предметаполе и кнопка для вычисления этих полей. Хорошо, это работает нормально, но я хочу сделать еще одну запись в текстовом поле, в которую я могу добавить процент разметки, например, для текстового поля 1 будет 50, а для текстового поля 2 я введу 3, тогда я бы хотелдобавьте значение разметки, скажем, 4%, чтобы при вычислении этих полей я получил итоговую сумму плюс процент добавленной разметки

Я могу заставить все работать, но не могу найти способ добавить эту надоедливую разметку

import UIKit

class CostingsViewController: UIViewController {

    //Item Cost entered into this field
    @IBOutlet weak var itemCost: UITextField!
    //Markup value entered into here
    @IBOutlet weak var markUP: UITextField!
    //This value is passed to this viewcontroller from another veiwcontroller
    @IBOutlet weak var newLabel: UILabel!
    //This value is calculated on the IBAction
    @IBOutlet weak var totalCost: UITextField!
    var finalName = ""

    override func viewDidLoad() {
        super.viewDidLoad()

     newLabel.text = finalName

        // Do any additional setup after loading the view.
    }

    @IBAction func calculateCost(_ sender: Any) {
       //Enter the markUP calculation here



        totalCost.text = String(format: "%.2f",Double(newLabel.text!)! * Double(itemCost.text!)!)

      self.view.endEditing(true)
    }
}
totalCost.text = String(format: "%.2f",Double(newLabel.text!)! * Double(itemCost.text!)!) 

это работает просто отлично, но отметьте, что я не могу заставить его работать - я проверил много уроков, но, кажется, есть много способов, но ни один из них не подходит для того, что я пытаюсь

Ответы [ 3 ]

0 голосов
/ 11 мая 2019

Математически, если вы хотите добавить 4% к значению, это означает умножение его на 1.04.

Это значит:

let itemCost = Double(itemCost.text!)!
let markUp = Double(markUP.text!)!

let total = itemCost * (1 + markUp / 100)
totalCost.text = String(format: "%.2f", total)

В качестве примечания, пожалуйста, используйте NumberFormatter для преобразования чисел в строку и наоборот, особенно когда мы говорим о валютах.

0 голосов
/ 11 мая 2019

Расчет общей суммы:

// the total, not rounded (e.g. if there was one item at a unit price of 0.10 and 3% markup (i.e. 0.03), this `unrounded` will have 0.103)

let unrounded = Double(quantity) * unitPrice * (markUp + 1.0) 

// You might then round that value to two decimal places, like so:

let grandTotal = (unrounded * 100.0).rounded(.toNearestOrAwayFromZero) / 100.0

С учетом сказанного я хотел бы предложить несколько других вещей:

  1. Вы можетеобратите внимание, что выше я не ссылаюсь на элементы управления UIKit, такие как текстовые поля и метки.Вы действительно хотите провести различие между «моделью» (цены, количества, итоги и т. Д.) И «видом» (текстовые поля, метки и т. Д.).

    • Объекты представления обычно, по соглашению, включают суффикс, который указывает тип объекта представления.Таким образом, вы можете иметь markUpTextField или quantityLabel.Таким образом, вы не только не перепутаете их с соответствующими значениями модели, но и можете четко сказать, что это за объект.

    • При обновлении текстового поля вам следуетобновить модель.Например, когда вы изменяете markUpTextField, вы обновляете объект числовой модели markUp.

    • Когда вы вычисляете сумму, вы должны рассчитывать ее только из объектов модели.Вы не должны ссылаться ни на какие UIKit объекты.

    Это не совсем критично, но это очень хорошая привычка, так как это центральный принцип шаблонов программирования MVC (и MVVM и MVP и ...).Преимущества этого действительно проявляются, когда вы в конечном итоге начинаете использовать представления таблиц / коллекций, где ваши элементы управления UIKit повторно используются для видимых элементов и больше не являются надежными источниками информации.Это также будет чрезвычайно полезно, когда вы начнете заниматься модульным тестированием своего кода, и вы извлекаете бизнес-логику из своих контроллеров представления и перемещаете их в некоторый посреднический объект, такой как «модель представления» или что-то еще.

  2. Следует избегать использования String(format:) для создания строк для пользовательского интерфейса.Вместо этого используйте NumberFormatter.Это решает две проблемы:

    • Вы хотите принимать и создавать «локализованные» числа в вашем пользовательском интерфейсе.Например, в Германии они пишут число от одного миллиона до двух десятичных знаков как 1.000.000,00.В Индии это может быть 10,00,000.00.И т. Д. Используя NumberFormatter, вы минимизируете сумму, которую вам необходимо кодировать для обработки всех этих международных форматов.

    • Если вы используете NumberFormatter с numberStyle из.percent для вашего значения разметки, он сделает необходимое деление для вас 100.

  3. Вы можете установить delegate дляUITextField объекты должны быть вашим контроллером представления (что вы можете сделать либо в IB, либо программно), а затем иметь расширение UITextFieldDelegate для вашего контроллера представления, чье shouldChangeCharactersIn примет изменение, только если полученный текст может быть изменен начисло с использованием вышеуказанных форматеров.

    Возможно, вам также понадобится textFieldDidEndEditing, который хорошо форматирует введенное значение, когда пользователь завершит работу.


Отражая вышеприведенноеВ результате наблюдений вы получите что-то вроде:

class CostingsViewController: UIViewController {

    // MARK: Outlets

    @IBOutlet weak var quantityLabel: UILabel!
    @IBOutlet weak var priceTextField: UITextField!
    @IBOutlet weak var markUpTextField: UITextField!
    @IBOutlet weak var totalLabel: UILabel!

    // MARK: Model objects

    var quantity: Int?  { didSet { updateTotal() } }
    var price: Double?  { didSet { updateTotal() } }
    var markUp: Double? { didSet { updateTotal() } }
    var total: Double?  { didSet { totalLabel.text = priceFormatter.string(for: total) } }

    // MARK: Private formatters

    private var priceFormatter: NumberFormatter = {
        let formatter = NumberFormatter()
        formatter.numberStyle = .decimal
        formatter.minimumFractionDigits = 2
        formatter.maximumFractionDigits = 2
        return formatter
    }()

    private var quantityFormatter: NumberFormatter = {
        let formatter = NumberFormatter()
        formatter.numberStyle = .decimal
        formatter.minimumFractionDigits = 0
        formatter.maximumFractionDigits = 0
        return formatter
    }()

    private var percentFormatter: NumberFormatter = {
        let formatter = NumberFormatter()
        formatter.numberStyle = .percent
        formatter.minimumFractionDigits = 0
        formatter.maximumFractionDigits = 2
        return formatter
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        // I'm going to set these here, but maybe these were supplied by the presenting view controller

        quantity = 3
        price = 1000
        markUp = 0

        // update the UI controls

        quantityLabel.text = quantityFormatter.string(for: quantity)
        priceTextField.text = priceFormatter.string(for: price)
        markUpTextField.text = percentFormatter.string(for: markUp)
        totalLabel.text = priceFormatter.string(for: total)
    }
}

private extension CostingsViewController {
    private func updateTotal() {
        // calculate total
        let quant = quantity ?? 0
        let cost = price ?? 0
        let percent = markUp ?? 0
        let unrounded = Double(quant) * cost * (percent + 1.0)

        // round the result
        let rounded = (unrounded * 100.0).rounded(.toNearestOrAwayFromZero) / 100.0

        // update our model
        total = rounded
    }
}

extension CostingsViewController: UITextFieldDelegate {
    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        // some useful constants
        let decimalSeparator = priceFormatter.decimalSeparator ?? "."
        let percentSymbol = percentFormatter.percentSymbol ?? "%"

        // figure out what the string value will be after replacing the characters
        let oldText = textField.text ?? ""
        let updateRange = Range(range, in: oldText)!
        let text = oldText.replacingCharacters(in: updateRange, with: string).filter(("01234567890" + decimalSeparator).contains)

        // update the appropriate model object
        switch textField {
        case priceTextField:
            if text == "" {
                price = 0
                return true
            } else if let value = priceFormatter.number(from: text)?.doubleValue {
                price = value
                return true
            } else {
                return false
            }

        case markUpTextField:
            if text == "" {
                markUp = 0
                return true
            } else if let value = percentFormatter.number(from: text + percentSymbol)?.doubleValue {
                markUp = value
                return true
            } else {
                return false
            }

        default:
            return true
        }
    }

    func textFieldDidEndEditing(_ textField: UITextField) {
        switch textField {
        case priceTextField:  textField.text = priceFormatter.string(for: price)
        case markUpTextField: textField.text = percentFormatter.string(for: markUp)
        default:              break
        }
    }
}

Еще одно уточнение: при создании типов данных для хранения цен я бы рекомендовал не использовать двоичные числа с плавающей запятой, такие как Float или * 1074.*.Эти типы не могут на самом деле идеально захватить дробные десятичные значения.Я бы использовал Decimal тип вместо этого.Это поможет избежать проблем округления, которые могут возникнуть, если вы начнете складывать много двоичных значений с плавающей запятой.

Если вы сделаете это, вы получите что-то вроде:

class CostingsViewController: UIViewController {

    // MARK: Outlets

    @IBOutlet weak var quantityLabel: UILabel!
    @IBOutlet weak var priceTextField: UITextField!
    @IBOutlet weak var markUpTextField: UITextField!
    @IBOutlet weak var totalLabel: UILabel!

    // MARK: Model objects

    var quantity: Int?   { didSet { updateTotal() } }
    var price: Decimal?  { didSet { updateTotal() } }
    var markUp: Decimal? { didSet { updateTotal() } }
    var total: Decimal?  { didSet { totalLabel.text = priceFormatter.string(for: total) } }

    // MARK: Private formatters

    private var priceFormatter: NumberFormatter = {
        let formatter = NumberFormatter()
        formatter.numberStyle = .decimal
        formatter.minimumFractionDigits = 2
        formatter.maximumFractionDigits = 2
        formatter.generatesDecimalNumbers = true
        return formatter
    }()
    private var quantityFormatter: NumberFormatter = {
        let formatter = NumberFormatter()
        formatter.numberStyle = .decimal
        formatter.minimumFractionDigits = 0
        formatter.maximumFractionDigits = 0
        return formatter
    }()
    private var percentFormatter: NumberFormatter = {
        let formatter = NumberFormatter()
        formatter.numberStyle = .percent
        formatter.minimumFractionDigits = 0
        formatter.maximumFractionDigits = 2
        formatter.generatesDecimalNumbers = true
        return formatter
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        // I'm going to set these here, but maybe these were supplied by the presenting view controller

        quantity = 3
        price = Decimal(1000)
        markUp = Decimal(0)

        // update the UI controls

        quantityLabel.text = quantityFormatter.string(for: quantity)
        priceTextField.text = priceFormatter.string(for: price)
        markUpTextField.text = percentFormatter.string(for: markUp)
        totalLabel.text = priceFormatter.string(for: total)
    }
}

private extension CostingsViewController {
    private func updateTotal() {
        // calculate total
        let quant = Decimal(quantity ?? 0)
        let cost = price ?? Decimal(0)
        let percent = markUp ?? Decimal(0)
        var unrounded = quant * cost * (percent + Decimal(1))

        // round the result
        var rounded = Decimal()
        NSDecimalRound(&rounded, &unrounded, 2, .bankers)

        // update our model
        total = rounded
    }
}

extension CostingsViewController: UITextFieldDelegate {
    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        // some useful constants
        let decimalSeparator = priceFormatter.decimalSeparator ?? "."
        let percentSymbol = percentFormatter.percentSymbol ?? "%"

        // figure out what the string value will be after replacing the characters
        let oldText = textField.text ?? ""
        let updateRange = Range(range, in: oldText)!
        let text = oldText.replacingCharacters(in: updateRange, with: string).filter(("01234567890" + decimalSeparator).contains)

        // update the appropriate model object
        switch textField {
        case priceTextField:
            if text == "" {
                price = Decimal(0)
                return true
            } else if let value = priceFormatter.number(from: text)?.decimalValue {
                price = value
                return true
            } else {
                return false
            }

        case markUpTextField:
            if text == "" {
                markUp = Decimal(0)
                return true
            } else if let value = percentFormatter.number(from: text + percentSymbol)?.decimalValue {
                markUp = value
                return true
            } else {
                return false
            }

        default:
            return true
        }
    }

    func textFieldDidEndEditing(_ textField: UITextField) {
        switch textField {
        case priceTextField:  textField.text = priceFormatter.string(for: price)
        case markUpTextField: textField.text = percentFormatter.string(for: markUp)
        default:              break
        }
    }
}

Наконец, как я упоминал выше, мы обычно хотели бы получить большую часть этого кода из контроллера представления (используя MVVP или MVP или что-то еще).Это выходит за рамки этого вопроса, но я упоминаю его для полноты картины.

0 голосов
/ 11 мая 2019

Если вы можете сказать

totalCost.text = String(format: "%.2f",Double(newLabel.text!)! * Double(itemCost.text!)!) 

Тогда вы можете сказать

let v1 = Double(newLabel.text!)!
let v2 = Double(itemCost.text!)!
let v3 = // do your math here
totalCost.text = String(format: "%.2f", v3)

Я не потворствую вашему коду; Я просто говорю, что вам не составит труда разбить его на части, чтобы вам было проще манипулировать значениями Double.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...