我正在尝试更新现有代码以使用逗号进行分组分隔符(即 10,000,000 而不是 10000000),如何用简单的方法实现此目的?我正在发布现有的代码。
我想要的只是在活动计算窗口(textFieldMain)和历史窗口(textViewHistory)中显示带逗号的格式化数字
import UIKit
class ViewController: UIViewController {
// MARK: Properties
@IBOutlet weak var textFieldMain: UITextField!
@IBOutlet weak var button0: UIButton!
@IBOutlet weak var button1: UIButton!
@IBOutlet weak var button2: UIButton!
@IBOutlet weak var button3: UIButton!
@IBOutlet weak var button4: UIButton!
@IBOutlet weak var button5: UIButton!
@IBOutlet weak var button6: UIButton!
@IBOutlet weak var button7: UIButton!
@IBOutlet weak var button8: UIButton!
@IBOutlet weak var button9: UIButton!
@IBOutlet weak var buttonPeriod: UIButton!
@IBOutlet weak var buttonPlusMinus: UIButton!
@IBOutlet weak var buttonEqual: UIButton!
@IBOutlet weak var buttonAddition: UIButton!
@IBOutlet weak var buttonSubstraction: UIButton!
@IBOutlet weak var buttonMultiplication: UIButton!
@IBOutlet weak var buttonDivision: UIButton!
@IBOutlet weak var buttonAllCancel: UIButton!
@IBOutlet weak var textViewHistory: UITextView!
var valueA: Double = 0
var valueB: Double = 0
var currentOperator: String = ""
var refreshTextField: Bool = true
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: Actions
@IBAction func allCancel(_ sender: UIButton) {
// reset all variables
valueA = 0
valueB = 0
currentOperator = ""
refreshTextField = true
// update text field
textFieldMain.text = "0"
}
@IBAction func clearHistory(_ sender: UIButton) {
// reset all variables
textViewHistory.text = ""
}
@IBAction func numberPress(_ sender: UIButton) {
// find text to be added
var value = sender.currentTitle!
// get current text
var currentText = ""
// value shourld be refreshed?
if textFieldMain.text != "0" && (!refreshTextField || value == "±") {
currentText = textFieldMain.text!
}
// zero
if currentText == "0" && value == "0" {
value = ""
}
// period
if value == "." {
if currentText == "" {
value = "0."
} else if currentText.range(of: ".") != nil {
value = ""
}
}
// plus/minus
if value == "±" {
value = ""
if currentText != "" {
if currentText.hasPrefix("-") {
let start = currentText.startIndex
let end = currentText.index(after: start)
currentText = currentText.replacingOccurrences(of: "-", with: "", range: start..<end)
} else {
currentText = "-\(currentText)"
}
}
}
// update text field
refreshTextField = false
textFieldMain.text = "\(currentText)\(value)"
}
@IBAction func operatorPress(_ sender: UIButton) {
// ignore if text field is empty
if textFieldMain.text != "" {
// get new operator
let newOperator = sender.titleLabel?.text!
// refresh text field
refreshTextField = true
if currentOperator == "" && newOperator != "=" {
// copy current value
valueA = Double(textFieldMain.text!)!
// update operator
currentOperator = newOperator!
} else {
// copy current value
valueB = Double(textFieldMain.text!)!
// perform operation
var result: Double = 0
switch currentOperator {
case "+":
result = valueA + valueB
case "-":
result = valueA - valueB
case "×":
result = valueA * valueB
case "÷":
result = valueA / valueB
default:
result = valueB
}
if currentOperator != "" {
// construct history entry
let historyEntry: String = "\(valueA) \(currentOperator) \(valueB) = \(result)\n"
// update history
textViewHistory.text! += historyEntry
}
// update operator & values
currentOperator = ""
valueA = 0
valueB = 0
if newOperator != "=" {
valueA = result
currentOperator = newOperator!
}
// update text field
textFieldMain.text = String(result)
}
}
}
}
所以我尝试制定这些格式规则...
private let auxFormatter: NumberFormatter = {
let formatter = NumberFormatter()
let locale = Locale.current
formatter.groupingSeparator = ""
formatter.decimalSeparator = locale.decimalSeparator
formatter.numberStyle = .decimal
formatter.maximumIntegerDigits = 100
formatter.minimumFractionDigits = 0
formatter.maximumFractionDigits = 100
return formatter
}()
private let auxTotalFormatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.groupingSeparator = ""
formatter.decimalSeparator = ""
formatter.numberStyle = .decimal
formatter.maximumIntegerDigits = 100
formatter.minimumFractionDigits = 0
formatter.maximumFractionDigits = 100
return formatter
}()
private let printFormatter: NumberFormatter = {
let formatter = NumberFormatter()
let locale = Locale.current
formatter.groupingSeparator = locale.groupingSeparator
formatter.decimalSeparator = locale.decimalSeparator
formatter.numberStyle = .decimal
formatter.maximumIntegerDigits = 9
formatter.minimumFractionDigits = 0
formatter.maximumFractionDigits = 8
return formatter
}()
对代码进行以下修改
@IBAction func numberPress(_ sender: UIButton) {
var value = sender.currentTitle!
if textFieldMain.text != "0" && (!refreshTextField || value == "±") {
value = textFieldMain.text!
}
if value == "." {
if textFieldMain.text == "" {
value = "0."
} else if textFieldMain.text!.contains(".") {
return
}
}
if value == "±" {
if textFieldMain.text != "" {
if textFieldMain.text!.hasPrefix("-") {
textFieldMain.text!.remove(at: textFieldMain.text!.startIndex)
} else {
textFieldMain.text = "-" + textFieldMain.text!
}
}
return
}
if refreshTextField {
textFieldMain.text = value
} else {
textFieldMain.text! += value
}
refreshTextField = false
}
@IBAction func operatorPress(_ sender: UIButton) {
if textFieldMain.text != "" {
let newOperator = sender.titleLabel?.text!
refreshTextField = true
if currentOperator == "" && newOperator != "=" {
valueA = auxFormatter.number(from: textFieldMain.text!)?.doubleValue ?? 0
currentOperator = newOperator!
} else {
valueB = auxFormatter.number(from: textFieldMain.text!)?.doubleValue ?? 0
var result: Double = 0
switch currentOperator {
case "+":
result = valueA + valueB
case "-":
result = valueA - valueB
case "×":
result = valueA * valueB
case "÷":
result = valueA / valueB
default:
result = valueB
}
if currentOperator != "" {
let historyEntry = "\(auxTotalFormatter.string(from: NSNumber(value: valueA))!) \(currentOperator) \(auxTotalFormatter.string(from: NSNumber(value: valueB))!) = \(printFormatter.string(from: NSNumber(value: result))!)\n"
textViewHistory.text! += historyEntry
}
currentOperator = ""
valueA = 0
valueB = 0
if newOperator != "=" {
valueA = result
currentOperator = newOperator!
}
textFieldMain.text = printFormatter.string(from: NSNumber(value: result))
}
}
}
但是现在所有输入的行为都是这样的...... 如果我按这三个数字 7 > 8 > 9 >,它会显示 77777777(如果我将其分解为 7 77 777 7777)
有很多方法可以实现这一点——您可能需要搜索现有的示例。
一些建议...
看起来您在值和字符串之间转换以及评估字符串太多了。
最好维护值,并且仅在需要显示时才对其进行格式化。
因此,首先,我们可以沿着这些思路思考。
假设我们有
var curVal: Double
并且我们已经检查了按钮的 .currentTitle
并将其转换为 btnVal: Double
...
curVal = 0
// user taps "5"
curVal *= 10.0
curVal += btnVal
// curVal now equals 5
// user taps "3"
curVal *= 10.0
curVal += btnVal
// curVal now equals 53
// user taps "7"
curVal *= 10.0
curVal += btnVal
// curVal now equals 537
因此,每次更改
curVal
的值时,我们都会使用 NumberFormatter
来更新“显示”标签。
如果我们输入负数,我们需要做一些不同的
curVal
操作。为此,如果 btnVal
小于零,我们将减去
curVal
:
// user taps "5"
curVal *= 10.0
curVal += curVal >= 0 ? btnVal : -btnVal
// curVal now equals 5
// user taps "+/-"
curVal *= -1.0
// curVal now equals -5
// user taps "3"
curVal *= 10.0
curVal += curVal >= 0 ? btnVal : -btnVal
// curVal now equals -53
// user taps "7"
curVal *= 10.0
curVal += curVal >= 0 ? btnVal : -btnVal
// curVal now equals -537
// user taps "+/-" again
curVal *= -1.0
// curVal now equals 537
NumberFormatter
会在需要时自动添加“-”...所以不再需要查看字符串是否以“-”开头或需要插入“-”。
当然,如果我们尝试输入小数点后的数字,这是行不通的!
因此,我们需要跟踪“十进制数”并将btnVal
除以10、100、1000等,然后再将其添加到
curVal
。让我们看看我们的按钮点击处理程序可能会是什么样子:
@objc func btnTapped(_ sender: UIButton) {
guard let title = sender.currentTitle else { return }
// if it's a number button
if let val = Double(title) {
var p: Double = 0
var btnVal: Double = val
// if "decimal count" is greater-than-Zero,
// we need to divide the input value by 10, 100, 1000, etc
if decCount > 0 {
p = pow(10.0, Double(decCount))
btnVal /= p
decCount += 1
} else {
currentValue *= 10.0
}
currentValue += currentValue >= 0 ? btnVal : -btnVal
// if we have decimal values, we need to round the result
// due to floating-point precision
// e.g. if we enter "0" "." "1" "2"
// the actual value is 0.12000000000000002048
// so we round to the current number of decimal places
if p > 0 {
currentValue = round(currentValue * p) / p
}
}
// if decimal button tapped
if title == decimalSym {
if decCount == 0 {
decCount = 1
decJustTapped = true
}
}
if title == plusMinusSym {
// we don't want to display "-0"
// so only do this if...
if currentValue != 0 {
currentValue *= -1
}
}
// clear the current value and
// reset decimal count
if title == "C" {
currentValue = 0
decCount = 0
}
if operators.contains(title) {
// perform operation...
}
udpateEntryLabel()
}
func udpateEntryLabel() {
// this will handle entering "." followed by one or more Zeroes
numFormatter.minimumFractionDigits = decCount - 1
var str = numFormatter.string(from: currentValue as NSNumber) ?? "0"
// this will only be true if the decimal symbol button was tapped
// but we do not yet have a fractional value
if decJustTapped {
decJustTapped = false
str += "."
}
entryLabel.text = str
}
这是一个快速、完整的示例...
简单按钮子类以获得“圆形”按钮:
class PillButton: UIButton {
override func layoutSubviews() {
super.layoutSubviews()
layer.cornerRadius = bounds.height * 0.5
}
}
示例视图控制器:
class CalcVC: UIViewController {
let entryLabel = UILabel()
let numFormatter: NumberFormatter = {
let nf = NumberFormatter()
nf.numberStyle = .decimal
// we want ".1" to be displayed as "0.1"
nf.minimumIntegerDigits = 1
return nf
}()
var currentValue: Double = 0
var decJustTapped: Bool = false
var decCount: Int = 0
// for clarity (tough to read division character and plus character)
// and, you'll probably want to localize the decimal character
let plusMinusSym: String = "±"
let pctSym: String = "%"
let divideSym: String = "+"
let multiplySym: String = "X"
let minusSym: String = "-"
let plusSym: String = "+"
let decimalSym: String = "."
let equalsSym: String = "="
lazy var operators: [String] = [
pctSym, divideSym, multiplySym, minusSym, plusSym, equalsSym
]
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBlue
let btn = UIButton()
let btnVal: Double = 0
var curVal: Double = 0
// user taps "5"
curVal *= 10.0
curVal += btnVal
// cVal now equals 5
// user taps "3"
curVal *= 10.0
curVal += btnVal
// cVal now equals 53
// user taps "7"
curVal *= 10.0
curVal += btnVal
// cVal now equals 537
// user taps "5"
curVal *= 10.0
curVal += btnVal
// cVal now equals 5
// user taps "+/-"
curVal *= -1.0
// cVal now equals -5
// user taps "3"
curVal *= 10.0
curVal += curVal >= 0 ? btnVal : -btnVal
// cVal now equals -53
// user taps "7"
curVal *= 10.0
curVal += curVal >= 0 ? btnVal : -btnVal
// cVal now equals -537
// user taps "+/-" again
curVal *= -1.0
// cVal now equals 537
let titles: [[String]] = [
["C", plusMinusSym, pctSym, divideSym],
["7", "8", "9", multiplySym],
["4", "5", "6", minusSym],
["1", "2", "3", plusSym],
["0", decimalSym, equalsSym],
]
let stackView = UIStackView()
stackView.axis = .vertical
stackView.spacing = 6
stackView.addArrangedSubview(entryLabel)
for (iRow, rowTitles) in titles.enumerated() {
let rowStack = UIStackView()
rowStack.axis = .horizontal
rowStack.distribution = .fillEqually
rowStack.spacing = 6
// handle bottom row differently because we have only 3 buttons
if iRow == titles.count - 1 {
let b = PillButton()
b.addTarget(self, action: #selector(btnTapped(_:)), for: .touchUpInside)
b.setTitle(rowTitles[0], for: [])
b.setTitleColor(.white, for: .normal)
b.setTitleColor(.lightGray, for: .highlighted)
b.backgroundColor = .darkGray
rowStack.addArrangedSubview(b)
let rowStack2 = UIStackView()
rowStack2.axis = .horizontal
rowStack2.distribution = .fillEqually
rowStack2.spacing = 6
for j in 1..<rowTitles.count {
let b = PillButton()
b.addTarget(self, action: #selector(btnTapped(_:)), for: .touchUpInside)
b.setTitle(rowTitles[j], for: [])
b.setTitleColor(.white, for: .normal)
b.setTitleColor(.lightGray, for: .highlighted)
b.backgroundColor = j == 1 ? .darkGray : .systemOrange
b.heightAnchor.constraint(equalTo: b.widthAnchor, multiplier: 1.0).isActive = true
rowStack2.addArrangedSubview(b)
}
rowStack.addArrangedSubview(rowStack2)
} else {
rowTitles.forEach { s in
let b = PillButton()
b.addTarget(self, action: #selector(btnTapped(_:)), for: .touchUpInside)
b.setTitle(s, for: [])
b.setTitleColor(.white, for: .normal)
b.setTitleColor(.lightGray, for: .highlighted)
b.backgroundColor = .darkGray
b.heightAnchor.constraint(equalTo: b.widthAnchor, multiplier: 1.0).isActive = true
rowStack.addArrangedSubview(b)
}
}
stackView.addArrangedSubview(rowStack)
}
// set background colors for non-darkGray buttons
for i in 0..<stackView.arrangedSubviews.count - 1 {
if let sv = stackView.arrangedSubviews[i] as? UIStackView,
let v = sv.arrangedSubviews.last {
v.backgroundColor = .systemOrange
}
}
if let sv = stackView.arrangedSubviews[1] as? UIStackView {
for i in 0..<3 {
sv.arrangedSubviews[i].backgroundColor = .lightGray
}
}
stackView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stackView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
stackView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
stackView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
stackView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -20.0),
])
// label properties
entryLabel.font = .monospacedDigitSystemFont(ofSize: 24.0, weight: .regular)
entryLabel.backgroundColor = UIColor(white: 0.95, alpha: 1.0)
entryLabel.textAlignment = .right
entryLabel.text = "0"
}
@objc func btnTapped(_ sender: UIButton) {
guard let title = sender.currentTitle else { return }
// if it's a number button
if let val = Double(title) {
var p: Double = 0
var btnVal: Double = val
// if "decimal count" is greater-than-Zero,
// we need to divide the input value by 10, 100, 1000, etc
if decCount > 0 {
p = pow(10.0, Double(decCount))
btnVal /= p
decCount += 1
} else {
currentValue *= 10.0
}
currentValue += currentValue >= 0 ? btnVal : -btnVal
// if we have decimal values, we need to round the result
// due to floating-point precision
// e.g. if we enter "0" "." "1" "2"
// the actual value is 0.12000000000000002048
// so we round to the current number of decimal places
if p > 0 {
currentValue = round(currentValue * p) / p
}
}
// if decimal button tapped
if title == decimalSym {
if decCount == 0 {
decCount = 1
decJustTapped = true
}
}
if title == plusMinusSym {
// we don't want to display "-0"
// so only do this if...
if currentValue != 0 {
currentValue *= -1
}
}
// clear the current value and
// reset decimal count
if title == "C" {
currentValue = 0
decCount = 0
}
if operators.contains(title) {
// perform operation...
}
udpateEntryLabel()
}
func udpateEntryLabel() {
// this will handle entering "." followed by one or more Zeroes
numFormatter.minimumFractionDigits = decCount - 1
var str = numFormatter.string(from: currentValue as NSNumber) ?? "0"
// this will only be true if the decimal symbol button was tapped
// but we do not yet have a fractional value
if decJustTapped {
decJustTapped = false
str += "."
}
entryLabel.text = str
}
}
运行时看起来像这样:请注意,仅实现了数字、小数、“C”和“+/-”按钮...我将把操作和“历史视图”任务留给您:)