How to make a full decimal point for a calculator on swift? - ios

I've just started to study Xcode.
I've made all digits and math signs, but have no clue how to make a dot for calculator.
This is what I've done (deleted some repeating parts of math operations):
class ViewController: UIViewController {
var numberFromScreen: Double = 0
var firstNum: Double = 0
var operation: Int = 0
var mathSign: Bool = false
#IBOutlet weak var result: UILabel!
#IBAction func digits(_ sender: UIButton) {
if mathSign == true {
result.text = String (sender.tag)
mathSign = false
}
else {
result.text = result.text! + String (sender.tag)
}
numberFromScreen = Double (result.text!)!
}
#IBAction func buttons(_ sender: UIButton) {
if result.text != "" && sender.tag != 10 && sender.tag != 15 {
firstNum = Double (result.text!)!
if sender.tag == 11 {// divine
result.text = "/"
}
operation = sender.tag
mathSign = true
}
else if sender.tag == 15 {// calculate
if operation == 11 {
result.text = String(firstNum / numberFromScreen)
}
}
else if sender.tag == 10 {
result.text = ""
firstNum = 0
numberFromScreen = 0
operation = 0
}
}
}

In your case using the NumberFormatter would be a good option.
You can define it as follows:
private var formater: NumberFormatter {
let formater = NumberFormatter()
formater.maximumIntegerDigits = 9 // Change this value
formater.maximumFractionDigits = 9 // Change this value
formater.minimumFractionDigits = 9 // Change this value
formater.minimumIntegerDigits = 1 // Change this value
formater.maximumIntegerDigits = 9 // Change this value
formater.groupingSeparator = " "
formater.locale = Locale.current
formater.numberStyle = .decimal
return formater
}
And when you are setting the result to the label, you can go:
result.text = formater.string(from: NSDecimalNumber(value: yourValue))
If you are making a calculator I would recommend you that for bigger numbers or for numbers with many decimal places, you set the numberStyle property to .scientific.

Related

Adding Double and a String to then display on a UILabel

I am trying to add up a value that is entered in the text field with a value specified as a double and then returning the value on a label. The code that I have is :
#IBOutlet weak var enterField: UITextField!
var weekOneTotal:Double = 0
#IBAction func addButton(_ sender: Any) {
addCorrectValue()
}
func addCorrectValue () {
guard let addAmount = convertAmount(input: enterField.text!) else {
print("Invalid amount")
return
}
let newValue = weekOneTotal += addAmount
secondScreen.weekOneAmountLabel.text = String(newValue)
}
func convertAmount (input:String) -> Double? {
let numberFormatter = NumberFormatter ()
numberFormatter.numberStyle = .decimal
return numberFormatter.number(from: input)?.doubleValue
}
Try this:
func addCorrectValue () {
guard let addAmount = Double(enterField.text!) else {
print("Invalid amount")
return
}
let newValue = weekOneTotal + addAmount
secondScreen.weekOneAmountLabel.text = "\(String(format: "%.1f", newValue))"
}
The .1 is the number of decimals that are shown. You can adjust that to your needs. Hope I understood the question and this works for you!
You probably want to increase value of weekOneTotal variable by converted amount and then you want to use this value as text of some label
weekOneTotal += addAmount
secondScreen.weekOneAmountLabel.text = String(weekOneTotal)

Error when add negative numbers in calculator

I have this code:
class MainViewController: UIViewController {
#IBOutlet weak var summaryLbl: UILabel!
var actualNumber: Double = 0
var previousNumber: Double = 0
var operationMath: Bool = false
var operation = 0
#IBAction func numberPressed(_ sender: UIButton) {
if operationMath == true {
summaryLbl.text = String(sender.tag)
actualNumber = Double(summaryLbl.text!)!
operationMath = false
} else {
if summaryLbl.text == "0" {
summaryLbl.text = ""
}
summaryLbl.text = summaryLbl.text! + String(sender.tag)
actualNumber = Double(summaryLbl.text!)!
}
}
#IBAction func buttons(_ sender: UIButton) {
if summaryLbl.text != "" && sender.tag != 10 && sender.tag != 17 {
previousNumber = Double(summaryLbl.text!)!
if sender.tag == 13 {
summaryLbl.text = "/"
} else if sender.tag == 14 {
summaryLbl.text = "x"
} else if sender.tag == 15 {
summaryLbl.text = "-"
} else if sender.tag == 16 {
summaryLbl.text = "+"
} else if sender.tag == 11 {
var number: Double = Double(summaryLbl.text!)!
number.negate()
let rounded = number.rounded()
summaryLbl.text = String(rounded).replacingOccurrences(of: ".0", with: "", options: .literal, range: nil)
}
operation = sender.tag
operationMath = true
} else if sender.tag == 17 {
var result: Double = 0
var rounded: Double = 0
if operation == 13 {
result = previousNumber / actualNumber
} else if operation == 14 {
result = previousNumber * actualNumber
} else if operation == 15 {
result = previousNumber - actualNumber
} else if operation == 16 {
result = previousNumber + actualNumber
} else if operation == 12 {
result = previousNumber.truncatingRemainder(dividingBy: actualNumber)
}
rounded = result.rounded()
if (result == rounded) {
summaryLbl.text = String(result).replacingOccurrences(of: ".0", with: "", options: .literal, range: nil)
} else {
summaryLbl.text = String(result)
}
} else if sender.tag == 10 {
summaryLbl.text = "0"
previousNumber = 0
actualNumber = 0
operation = 0
}
}
override func viewDidLoad() {
super.viewDidLoad()
summaryLbl.text = "0"
previousNumber = 0
actualNumber = 0
operation = 0
}
}
This is simple calculator.
I have a problem with calculations.
When I click the buttons, for example: 2 + 5 * -
then the application turns off with an error. When I enter such a key combination: 2 + 5 =
This calculation will be done correctly.
 
How do I add commas to numbers?
Does anyone know how to fix the above problems?
A calculator is a Finite State Machine. It can be very complex but in its simplest form it resembles this:
So if we keep things simple and take the above machine as our target, after 2 + 5, our machine expects equals(=) to calculate the result or if an operator is added (like * in our case) it will expect a digit next. giving an operator (minus in our case) will result in an error.
The complexity is limited only by your imagination. You can add support for decimal point numbers, brackets, powers etc. The more sugar you want to add the more complex the FSM will become.
I suggest starting with the simplest one. Maintain your states, the transitions allowed next and error handling in case of wrong transition.
Check this repo on github for Finite State Machine in swift: https://github.com/vishalvshekkar/SwiftFSM
And the corresponding article:
https://blog.vishalvshekkar.com/finite-state-machine-in-swift-ba0958bca34f

Adding Thousand Separator Automatically (Swift)

I have a code for my calculator
How could I do so that when the user would enter the numbers he would be separated by a space automatically?
I've been trying to find an answer for a long time, but nothing fits that it was displayed right away
var currentInput: Double {
get {
return Double (displayResultLabel.text!)!
}
set {
let value = "\(newValue)"
let ValueArray = (value.components(separatedBy:"."))
if ValueArray[1] == "0" {
displayResultLabel.text = "\(ValueArray[0])"
} else {
displayResultLabel.text = "\(newValue)"
}
stillTyping = false
}
}
#IBAction func numberPressed(_ sender: UIButton) {
let number = sender.currentTitle!
if stillTyping {
if (displayResultLabel.text?.characters.count)! < 14 {
displayResultLabel.text = displayResultLabel.text! + number
}
} else {
displayResultLabel.text = number
stillTyping = true
}
}
Then what happened:
#IBAction func numberPressed(_ sender: UIButton) {
let number = sender.currentTitle!
if stillTyping {
if (displayResultLabel.text?.characters.count)! < 14 {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
let newNumber = NSNumber(value: Double(displayResultLabel.text! + number)!)
displayResultLabel.text = formatter.string(from: newNumber)
}
} else {
displayResultLabel.text = number
stillTyping = true
}
}
Error
var stillTyping = false
var dotIsPlaced = false
var firstOperand: Double = 0
var secondOperand: Double = 0
var operationSign: String = ""
It is better to accumulate your value in a separate string that doesn't have the formatting applied rather than using the text field as your data model. You can then format the decimal and display it in the label as required using a NumberFormatter:
let formatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
return formatter
}()
var currentInput: String = "0" {
didSet {
self.displayResultLabel?.text = self.currentDisplay
}
var currentValue: Double {
return Double(self.currentInput) ?? 0
}
var currentDisplay: String {
return formatter.string(from: NSNumber(value:self.currentValue)) ?? "0"
}
func addDigit(_ digit: Int) {
if currentInput.count < 14 {
let newValue = self.currentValue * 10 + Double(digit)
self.currentInput = "\(newValue)"
}
}
#IBAction func numberPressed(_ sender: UIButton) {
guard let digit = Int(sender.currentTitle!) else {
return
}
self.addDigit(digit)
}
This is what NumberFormatters are for
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
if let newNumber = formatter.number(from: displayResultLabel.text! + number){
displayResultLabel.text = formatter.string(from: newNumber)
}
Note that NumberFormatters go both ways, and you can (and probably should) use them to parse numbers from strings, too

How not to show 0´s in label?

I have some buttons that print some text in a label when pushed. I also have a counter in front of the text to show how many times the respective button is pushed. But if some of the buttons isn´t pushed, it shows 0 in the label. Is it possible to convert the initial value on the int to blank space?
I have tried something like this:
var addCount:int = 0
var blankSpace = ""
if addCount == 0 {
addcount = blankSpace
}
else {
....
}
I have also tried:
if addCount == 0 {
addCount = String (blankSpace)
}
else {
}
and I have tried:
if addCount == 0 {
addCount = String ("")
}
I´m probably going about this all wrong, and I would appreciate help with this.
*
*
*
Edit: I thought there was some magic trick in swift to make the 0 disappear, so I took some shortcuts when I asked my question. Here is the whole setup:
I have four buttons in View Controller 1 that prints some text and adds 1 to counter. The data is based through a Swift file allSum.
The label that displays the text and counter is in View Controller 2.
allSum:
import UIKit
class allSum: NSObject {
var btn1Pressed:String = ""
var addToBtn1:Int = 0
var btn2Pressed:String = ""
var addToBtn2:Int = 0
var btn3Pressed:String = ""
var addToBtn3:Int = 0
var btn4Pressed:String = ""
var addToBtn4:Int = 0
func printToLabel() -> String{ //return a string with the current status
return ("\(addToBtn1) \(btn1Pressed + "")") + ("\(addToBtn2) \(btn2Pressed + "")") + ("\(addToBtn3) \(btn3Pressed + "")") + ("\(addToBtn4) \(btn4Pressed + "")")
}
View Controller 1
var total = allSum
var button1 = "Button 1 Pressed\n"
var button2 = "Button 2 Pressed\n"
var button3 = "Button 3 Pressed\n"
var button4 = "Button 4 Pressed\n"
#IBAction func no1(_ sender: UIButton) {
total.btn1Pressed = button1
toal.addToBtn1 += 1
}
#IBAction func no2(_ sender: UIButton) {
total.btn2Pressed = button2
toal.addToBtn2 += 1
}
#IBAction func no3(_ sender: UIButton) {
total.btn3Pressed = button3
toal.addToBtn3 += 1
}
#IBAction func no4(_ sender: UIButton) {
total.btn4Pressed = button4
toal.addToBtn4 += 1
}
View Controller 2
var total = allSum?
label.text = total.printToLabel()
With this setup, the label is showing 0 0 0 0 when no buttons are pressed.
So, my problem is that I can't set the label.text = "" since I also have other variables printed to it.
Sorry for my initial laziness
Set the actual label text as a blank space.
var addCount:Int = 0
var blankSpace = ""
if addCount == 0 {
yourlabel.text = blankSpace
} else {
yourlabel.text = "\(addCount)"
}
UPDATED:
Here's a way to write your current code with less code and fix your issue of changing 0's to blank strings
AllSum
class AllSum {
var sumsArray: [Int] = [0,0,0,0]
func stringForLabel() -> String { //return a string with the current status
var string = ""
for (index, value) in self.sumsArray.enumerated() {
if value > 0 {
string = string + "\(value) Button \(index + 1) was pressed\n"
}
}
return string
}
}
View Controller 1
You can use tags in UIBuilder, to match the indices in your array. Link up all buttons to the same function
var total = AllSum()
#IBAction func numberPressed(_ sender: UIButton) {
total.sumsArray[sender.tag] = sumsArray[sender.tag] + 1
}
View Controller 2
var total = AllSum()
label.text = total.stringForLabel()
You won't be able to assign a string, i.e. "", to a variable you've declared as an Int. In this case you'd be better off dealing with this at the time that you set the text on the label. You can use a simple if/else statement, or a ternary operator if you want a one-liner:
if addCount == 0 {
self.label.text = ""
} else {
self.label.text = "\(addCount)"
}
And for the ternary option:
self.label.text = addCount == 0 ? "" : "\(addCount)"
You're trying to set an Int to a String. Swift is a type safe language and doesn't let you do that. If you need, for some reason, to have the same variable represent both states, you can use an enum with an associated value and still use it as long as it is Describable.
Instead you can just have a computed value that returns a String that returns a blank String if the variable is zero:
func displayCount () -> String {
return addCount == 0 ? String() : String(describing:addCount)
}

How to make a number not repeat itself more than 2 times?

everyone ! Lets say we have
let random = arc4random_uniform(6)
how do i make it not repeat the same number more then two times ? I tried doing it like this :
let previousNumber = Int()
let lastNumber = Int ()
let random = Int(arc4random_uniform(6))
if random == previousNumber {
lastNumber = previousNumber
} else {
previousNumber = random
}
if random == lastNumber {
random = Int(arc4random_uniform(6))
}
But it didn't work. I am new to swift and i didn't find a topic about this on the new swift 3 code. Thank you !
First of all lets build a class to save the recent history of the selected values
class History {
private let size: Int
private var values = [Int]()
init(size:Int) {
self.size = size
}
func add(value: Int) {
values.insert(value, at: 0)
if values.count > size {
values.removeLast()
}
}
var repeatedValueOnFullHistory: Int? {
guard Set(values).count <= 1 else { return nil }
return values.first
}
}
Next let build a Randomizer
class Randomizer {
private var allValues = [Int]()
private var history: History
init?(maxValue: Int) {
guard maxValue > 0 else { return nil }
self.allValues = Array(0...maxValue)
self.history = History(size: maxValue + 1)
}
var next: Int {
let excludedValue = history.repeatedValueOnFullHistory
let allowedValues = allValues.filter { excludedValue != $0 }
let randomIndex = Int(arc4random_uniform(UInt32(allowedValues.count)))
let nextValue = allowedValues[randomIndex]
history.add(value: nextValue)
return nextValue
}
}
And finally let test it
if let r = Randomizer(maxValue: 6) {
r.next // 6
r.next // 2
r.next // 1
r.next // 4
r.next // 6
r.next // 4
r.next // 1
}

Resources