I tried to convert a value to another (from angstrom to arshin, a Russian ancient value of length), but often I got result in exponential style 1.837684e-10. I need to show it in decimal style 0.0000000001837.
The code I use:
let formatter = NumberFormatter()
formatter.locale = Locale.current
formatter.numberStyle = NumberFormatter.Style.decimal
if let text = textField.text, let number = formatter.number(from: text) {
angstrem = number.doubleValue
arshin = Double(angstrem * 7112000000.0000)
arshinLabel.text = "\(arshin)"
}
Adding this line is got nothing:
formatter.numberStyle = NumberFormatter.Style.decimal
UPD
Full code down there:
import UIKit
class AngstremViewController: UIViewController {
var angstrem: Double = 0
var arshin: Double = 0
var versta: Double = 0
var vershok: Double = 0
var decimetr: Double = 0
var duim: Double = 0
var kilometr: Double = 0
var metr: Double = 0
var mili: Double = 0
var millimetr: Double = 0
var mormil: Double = 0
var sajen: Double = 0
var santimetr: Double = 0
var fut: Double = 0
var yard: Double = 0
#IBOutlet weak var textField: UITextField!
//Result labels
#IBOutlet weak var angstremLabel: UILabel!
#IBOutlet weak var arshinLabel: UILabel!
#IBOutlet weak var verstaLabel: UILabel!
#IBOutlet weak var vershokLabel: UILabel!
#IBOutlet weak var decimetrLabel: UILabel!
#IBOutlet weak var duimLabel: UILabel!
#IBOutlet weak var kilometrLabel: UILabel!
#IBOutlet weak var metrLabel: UILabel!
#IBOutlet weak var miliLabel: UILabel!
#IBOutlet weak var millimetrLabel: UILabel!
#IBOutlet weak var mormiliLabel: UILabel!
#IBOutlet weak var sajenLabel: UILabel!
#IBOutlet weak var santimetrLabel: UILabel!
#IBOutlet weak var futLabel: UILabel!
#IBOutlet weak var yardLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let toolBar = UIToolbar()
toolBar.sizeToFit()
toolBar.tintColor = UIColor.init(red:47/255.0, green:158/255.0, blue:249/255.0, alpha: 1.0)
let flexibleSpace = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.flexibleSpace, target: nil, action: nil)
let doneButon = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.done, target: self, action: #selector(self.doneClicked))
toolBar.setItems([flexibleSpace, doneButon], animated: true)
textField.inputAccessoryView = toolBar
}
#objc func doneClicked() {
view.endEditing(true)
let formatter = NumberFormatter()
formatter.locale = Locale.current
formatter.numberStyle = NumberFormatter.Style.decimal
if let text = textField.text, let number = formatter.number(from: text) {
angstrem = number.doubleValue
arshin = Double(angstrem * 0.00000000014060742)
versta = Double(angstrem * 0.00000000000009373828270000001)
vershok = Double(angstrem * 0.0000000022497188)
decimetr = Double(angstrem * 0.000000001)
duim = Double(angstrem * 0.0000000039370078700000005)
kilometr = Double(angstrem * 0.0000000000001)
metr = Double(angstrem * 0.0000000001)
mili = Double(angstrem * 0.000000000000062137119)
millimetr = Double(angstrem * 0.0000001)
mormil = Double(angstrem * 0.00000000000005399568)
sajen = Double(angstrem * 0.000000000046869141)
santimetr = Double(angstrem * 0.00000001)
fut = Double(angstrem * 0.00000000032808399)
yard = Double(angstrem * 0.00000000010936132999999999)
arshinLabel.text = "\(arshin)"
verstaLabel.text = "\(versta)"
vershokLabel.text = "\(vershok)"
decimetrLabel.text = "\(decimetr)"
duimLabel.text = "\(duim)"
kilometrLabel.text = "\(kilometr)"
metrLabel.text = "\(metr)"
miliLabel.text = "\(mili)"
millimetrLabel.text = "\(millimetr)"
mormiliLabel.text = "\(mormil)"
sajenLabel.text = "\(sajen)"
santimetrLabel.text = "\(santimetr)"
futLabel.text = "\(fut)"
yardLabel.text = "\(yard)"
}
}
}
If i convert this
let number = formatter.number(from: text)
to
let number = formatter.String(from: number)
I've got errors.
I understand method but in all cases it doesn't working.
Could someone help with it?
So first of all what you did does not use a formatter at all (as already mentioned by #Martin R). You need to use formatter.string(from: <#number#>). But it will not work this way either, it will most likely display "0". To simulate your result you would need to use .scientific style.
So what you are looking for is .decimal style that includes minimumFractionDigits which will force displaying a number of digits you need. Another probably better way is using minimumSignificantDigits. This will always show at least N digits that are non-zero (kind-of, 1001 still counts as 4 non-zero).
Another issue is that when dealing with such values you should not be using double at all. You need NSDecimalNumber which is designed for precision and large values.
Check this following example that combines it all:
let firstValue = NSDecimalNumber(string: "0.0000000001837")
let secondValue: NSDecimalNumber = 0.00001
let overallValue = firstValue.multiplying(by: secondValue)
let formatter = NumberFormatter()
formatter.locale = .current
formatter.numberStyle = .decimal
formatter.minimumSignificantDigits = 4
if let text = formatter.string(from: overallValue) {
print(text)
}
This will print the result of 0,000000000000001837. If I increase minimumSignificantDigits to 6 then the result will be 0,00000000000000183700.
I've converted you code in playground that you can find helpful:
let formatter = NumberFormatter()
formatter.locale = Locale.current
formatter.numberStyle = NumberFormatter.Style.decimal
let text = "0.0000000001837"
//! Convert string to number
let number = formatter.number(from: text)!
// 1.837e-10
let stringFromNumber = formatter.string(from: number)
//0
// Set up maximum number of fractional digits in formatter
formatter.maximumFractionDigits = 100
let fullStringFromNumber = formatter.string(from: number)
//"0.0000000001837"
let angstrem = number.doubleValue
let arshin = Double(angstrem * 7112000000.0000)
//1.3064744
let arshinNumber = NSNumber(value: arshin)
let arshinStr = formatter.string(from: arshinNumber)
//"1.3064744"
Thanks to #matic-oblak !
The solution is:
let formatter = NumberFormatter()
formatter.locale = Locale.current
formatter.numberStyle = .decimal
formatter.minimumSignificantDigits = 6
if let text = textField.text, let number = formatter.number(from: text) {
angstrem = number.doubleValue
arshinLabel.text = formatter.string(from: NSDecimalNumber(value: angstrem).multiplying(by: 0.00000000014060742))
}
Related
This question already has an answer here:
Limit formatted Measurement to 2 digits
(1 answer)
Closed 4 years ago.
I'm making a temperature slider but the output keeps on displaying 15 or so digits after the decimal place. How do I resolve this and have it display only 4 numbers after the decimal place?
import UIKit
class TemperatureViewController : UIViewController {
#IBOutlet weak var fahrenheitLabel: UILabel!
#IBOutlet weak var celsiusLabel: UILabel!
#IBOutlet weak var kelvinLabel: UILabel!
#IBOutlet weak var temperatureSlider: temperatureSlider!
var temperature:Measurement<UnitTemperature> {
let fahrenheitValue = Measurement<UnitTemperature>(value: Double(temperatureSlider.value), unit: UnitTemperature.celsius)
return fahrenheitValue
}
#IBAction func sliderChange(_ sender: Any) {
updateTemperatureDisplay()
}
func updateTemperatureDisplay() {
celsiusLabel.text = temperature.description
fahrenheitLabel.text = temperature.converted(to: .fahrenheit).description
kelvinLabel.text = temperature.converted(to: .kelvin).description
}
override func viewDidLoad() {
updateTemperatureDisplay()
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
In most cases, you should make use of the available formatters. The measurements API provides a MeasurementFormatter which is a good place to start
let celsius = Measurement<UnitTemperature>(value: 10.91235, unit: UnitTemperature.celsius)
let fahrenheit = celsius.converted(to: .fahrenheit)
let mf = MeasurementFormatter()
mf.unitOptions = .providedUnit
mf.string(from: celsius)
mf.string(from: fahrenheit)
So the above example basically outputs
10.91235 °C // original
51.64222999999585 °F // converted
10.912°C
51.642°F
If you need more control, you can supply your own NumberFormatter to MeasurementFormatter
let numberFormatter = NumberFormatter()
numberFormatter.maximumFractionDigits = 4
numberFormatter.minimumFractionDigits = 2
let mf = MeasurementFormatter()
mf.numberFormatter = numberFormatter
mf.unitOptions = .providedUnit
You could extend Double (and use it throughout your app if this is an operation you need done elsewhere):
extension Double {
func truncate(places: Int) -> Double {
return Double(floor(pow(10.0, Double(places)) * self)/pow(10.0, Double(places)))
}
}
let rawValue = 51.64222999999585
let formattedValue = rawValue.truncate(places: 4)
print(formattedValue) // 51.6422
Apologies for this one, I'm sure that I'm missing a swift fundamental here.
I have a navigation app that I'm working on in swift.
Earlier on I made it as a single view and have changed it to a tab bar controller application.
In the background the location manager does its work and when is does the update locations work it displays the lat and long on a label.
When the app was a single view app this was no problem however now that it is a tab bar app, as soon as I change to a different tab when the location manager function fires it crashes the app with the following code (which I suspect means nothing much on its own) EXC_BREAKPOINT (code=1, subcode=0x10039bb54
I strongly suspect that I am doing something fundamentally wrong with trying to update a label on a view controller that is not active at the time.
distanceLabel.text = "Currently \(distanceText) from destination"
lbl_location.text = String(mylat) + ", " + String(mylong)
I have copied in all of my code below only taking out a few sensitive bits so that you can see the big picture.
Open to constructive feedback as to better coding practices.
Thanks
Bruce
import UIKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate {
//in the original view controller which has now been incorporated into tabs
#IBOutlet weak var distanceLabel: UILabel!
#IBOutlet weak var lbl_location: UILabel!
//on a new view controller on another tab
#IBOutlet weak var employeeNameSetup: UITextField!
#IBOutlet weak var employeeNumberSetup: UITextField!
#IBOutlet weak var contactNumberSetup: UITextField!
#IBOutlet weak var managerNameSetup: UITextField!
#IBOutlet weak var managerEmailSetup: UITextField!
#IBOutlet weak var adminPasswordSetup: UITextField!
#IBAction func setAppDefaults(_ sender: Any) {
if (employeeNameSetup.text?.isEmpty)! || (employeeNumberSetup.text?.isEmpty)! || (contactNumberSetup.text?.isEmpty)! || (managerNameSetup.text?.isEmpty)! || (managerEmailSetup.text?.isEmpty)!{
let noGoAlert = UIAlertController(title: "Insufficient Fields Filled out", message: "Only the Password field is optional, please fill out all other fields before pressing button", preferredStyle: .alert)
let defaultAction = UIAlertAction(title: "Close Alert", style: .default, handler: nil)
noGoAlert.addAction(defaultAction)
present(noGoAlert, animated: true, completion: nil)
}
else{
let defaults = UserDefaults.standard
defaults.set(employeeNameSetup.text, forKey: "employeeName")
defaults.set(employeeNumberSetup.text, forKey: "employeeNumber")
defaults.set(contactNumberSetup.text, forKey: "contactNumber")
defaults.set(managerNameSetup.text, forKey: "managerName")
defaults.set(managerEmailSetup.text, forKey: "managerEmail")
}
}
#IBAction func submit_button(_ sender: Any) {
uploadData()
}
let location_process = CLLocationManager()
var mylat :Double = 0
var mylong :Double = 0
var destination = CLLocation(latitude: #####, longitude: #####)
var distanceToDest :Int = 0
var dburl = NSURL(string: "#########")
let contact_name = "#####"
let contact_number = "#####"
let unique_id = "###"
let employee_number = "#####"
var distanceText = ""
var runOnce = false
var isAdmin = false
var adminPassword = "#######"
func sendEmail(messageText: String){
let url = URL (string: "########")
let session = URLSession.shared
var request = URLRequest(url: url!)
request.httpMethod = "POST"
let paramtosend = #########
request.httpBody = paramtosend.data(using: String.Encoding.utf8)
let task = session.dataTask(with: request)
task.resume()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let mylocation = locations[0]
mylat = mylocation.coordinate.latitude
mylong = mylocation.coordinate.longitude
distanceToDest = Int(mylocation.distance(from: destination))
if distanceToDest > 1000 {
distanceText = String(Int(distanceToDest/1000))
distanceText = distanceText + " km"
}
else if distanceToDest < 100 {
let current_date_time = Date()
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .medium
formatter.dateFormat = "HH:mm dd MMM yyyy"
formatter.timeZone = TimeZone.autoupdatingCurrent
let timestamp = formatter.string(from: current_date_time)
if !runOnce {
sendEmail(messageText: "\(contact_name); Employee \(employee_number) Arrived at destination \(destination.coordinate.latitude), \(destination.coordinate.longitude) at \(timestamp)")
}
distanceText = String(distanceToDest)
distanceText = distanceText + " m"
runOnce = true
}
else {
distanceText = String(distanceToDest)
distanceText = distanceText + " m"
}
distanceLabel.text = "Currently \(distanceText) from destination"
lbl_location.text = String(mylat) + ", " + String(mylong)
uploadData()
}
#IBAction func uploadData()
{
var httprequest = URLRequest(url: dburl! as URL)
httprequest.httpMethod = "POST"
var dataString = "####" // starting POST string with a secretWord
// the POST string has entries separated by &
dataString = dataString + "&unique_id=" + unique_id
dataString = dataString + "&mylat=" + String(mylat) // add items as name and value
dataString = dataString + "&mylong=" + String(mylong)
dataString = dataString + "&contact_name=" + contact_name
dataString = dataString + "&contact_number=" + contact_number
dataString = dataString + "&employee_number=" + employee_number
let current_date_time = Date()
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .medium
formatter.dateFormat = "HH:mm dd MMM yyyy"
formatter.timeZone = TimeZone.autoupdatingCurrent
let timestamp = formatter.string(from: current_date_time)
dataString = dataString + "×tamp=" + timestamp
// convert the post string to utf8 format
let dataD = dataString.data(using: .utf8) // convert to utf8 string
do
{
// the upload task, uploadJob, is defined here
let uploadJob = URLSession.shared.uploadTask(with: httprequest, from: dataD!)
//let uploadJob = URLSession.shared.uploadTask(with: httprequest, from: dataD)
uploadJob.resume()
}
}
override func viewDidLoad() {
super.viewDidLoad()
location_process.delegate = self
location_process.pausesLocationUpdatesAutomatically = false
location_process.desiredAccuracy = kCLLocationAccuracyBest
location_process.requestWhenInUseAuthorization()
location_process.requestAlwaysAuthorization()
if CLLocationManager.locationServicesEnabled()
{
location_process.distanceFilter = 50
location_process.allowsBackgroundLocationUpdates = true
location_process.startUpdatingLocation()
}
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
i have 3 UISegmentedControl in one UIView, i need to make some cals when the user select one UISegmentedControl, for this i try to use
#IBOutlet weak var segmentedController1: UISegmentedControl!
#IBOutlet weak var segmentedController2: UISegmentedControl!
#IBOutlet weak var segmentedController3: UISegmentedControl!
The code for first segmentedController1 is ok use:
let segmendIndex1 = segmentedController1.selectedSegmentIndex
if segmendIndex1 == 0 {
let convertedValue = textFieldVal * untDollars
//Parseo a String
let initValue = String(format: "%.2f", textFieldVal)
let endValue = String(format: "%.2f", convertedValue)
resultadoLbl.text = "\(initValue) EUR = \(endValue) USD"
} else if segmendIndex1 == 1 {
let convertedValue = textFieldVal * unitEuros
let initValue = String(format: "%.2f", textFieldVal)
let endValue = String(format: "%.2f", convertedValue)
resultadoLbl.text = "\(initValue) USD = \(endValue) EUR "
}
but when i select the second segmentedController2 is no ready.
who the best way to make this use If ....Else IF etc...
You can set tag property of all the three segmentcontrol and in the same method use switch case to differentiate them.
I am working on a loan app, where I am struggling to figure out how to format the text fields as soon as the user types. It would be nice to see that because the outputs are calculated as soon as there is a digit in the textfield.
In my ViewController:
#IBOutlet var loanTextField: UITextField!
#IBOutlet var interestTextField: UITextField!
#IBOutlet var interestStepper: UIStepper!
#IBOutlet var termTextField: UITextField!
#IBOutlet var termStepper: UIStepper!
Created delegates to structure maximum lengths and allow decimals when it's applicable:
var integerTextFieldDelegate = NumericTextFieldDelegate(maxLength: 2)
var loanTextFieldDelegate = NumericTextFieldDelegate(maxLength: 10, allowDecimal: true)
var interestRateTextFieldDelegate = NumericTextFieldDelegate(maxLength: 2, allowDecimal: true)
Delegates are defined for each text fields in viewDidLoad:
loanTextField.text = ""
loanTextField.delegate = loanTextFieldDelegate
interestRateTextField.setTextNumericValue(DefaultInterestRate)
interestRateTextField.delegate = interestRateTextFieldDelegate
termTextField.setTextNumericValue(DefaultTerm)
termTextField.delegate = interestRateTextFieldDelegate
NumericTextFieldDelegate are referred to an extension:
// Implementation of UITextFieldDelegate that prevents non-numeric characters
// from being entered in a numeric text field.
public class NumericTextFieldDelegate: NSObject, UITextFieldDelegate {
let maxLength: Int
let allowDecimal: Bool
public init(maxLength: Int, allowDecimal: Bool = false) {
self.maxLength = maxLength
self.allowDecimal = allowDecimal
}
public func textField(textField: UITextField!,
shouldChangeCharactersInRange range: NSRange,
replacementString string: String!) -> Bool
{
let originalText: NSString = textField.text
let proposedText: NSString = originalText.stringByReplacingCharactersInRange(range, withString: string)
let proposedLength = proposedText.length
if proposedLength > maxLength {
return false
}
if allowDecimal {
if proposedLength > 0 && !isValidDoubleString(proposedText) {
return false
}
}
else {
if proposedLength > 0 && !isValidIntegerString(proposedText) {
return false
}
}
return true
}
}
I also have a number formatter extension:
public extension Double {
var formatted: String {
let formatter = NSNumberFormatter()
formatter.numberStyle = NSNumberFormatterStyle.CurrencyStyle
formatter.currencyGroupingSeparator?
formatter.minimumFractionDigits = 2
formatter.maximumFractionDigits = 2
return formatter.stringFromNumber(self)!
}
}
My question due to my lack of experience. How do I format the text field for loan ($ currency, 2 decimals and grouping separator) and interest rate (3 decimals at all times since it's in the increment of 1/8th) using my extensions. I seen other shouldChangeCharactersInRange method or having some blank UILabel to display the formatted text...but I'm unsure how to do it in Swift given my current set up. Thanks for any helpful tip!!
I was making a calculator app where in the display I wanted to format the number as user typing in with NSNumberFormatterStyle.DecimalStyle, so my code was like this when the user presses 1:
var addOne:Character = "1"
userInput.text.append(addOne)
var formatNumber:NSNumber = userInput.text.toInt()!
var formatter = NSNumberFormatter()
formatter.numberStyle = NSNumberFormatterStyle.DecimalStyle
var formattedNumber = formatter.stringFromNumber(formatNumber)!
userInput.text = "\(formattedNumber)"
But it didn't quite work because after formatting a number like 1,111 it gives me an error.
Now I understand the error that formattedNumber is not a number anymore so it gives nil value. To fix that I wrote this code:
var addOne:Character = "1"
userInput.text.append(addOne)
var getString:NSString = input.text
var newString = getString.stringByReplacingOccurrencesOfString(",", withString: "")
var formatNumber:NSNumber = newString.toInt()!
var formatter = NSNumberFormatter()
formatter.numberStyle = NSNumberFormatterStyle.DecimalStyle
var formattedNumber = formatter.stringFromNumber(formatNumber)!
userInput.text = "\(formattedNumber)"
It removes ,s for the NSNumber, and NSNumber stays a number which can be formatted as many times you want.
and You can setup maximum number to you textField like this:
formatter.maximumIntegerDigits = 12
it will work since you are displaying a formatted number
I have a function that gives me a time when the app is opened but I don't want it to be static I want it to be dynamic, updating every second. I thought of it being in a loop, but I don't know how to go about doing it. Doing a loop could work but if you have a better way of doing it please answer. Here is my function:
func timeNowString() -> NSString {
let date = NSDate()
var outputFormat = NSDateFormatter()
outputFormat.locale = NSLocale(localeIdentifier:"en_US")
outputFormat.dateFormat = "HH:mm:ss"
let timeString = outputFormat.stringFromDate(date)
return timeString;
}
Question: How do I make this function dynamic? So it goes and updates every second instead of it being a static label.
If you have any questions please comment down below.
Try this. You'll need to add a label and a button and hook them up to the textLabel and start IBAction. You can modify this to add hours (just minutes/seconds here) and add a timer.invalidate() where you need it.
import UIKit
class ViewController: UIViewController {
#IBOutlet var timeLabel: UILabel!
#IBAction func start(sender: AnyObject) {
var timer = NSTimer()
if !timer.valid {
let selector : Selector = "countTime"
timer = NSTimer.scheduledTimerWithTimeInterval(0.01, target:self, selector: selector, userInfo: nil, repeats: true)
startTime = NSDate.timeIntervalSinceReferenceDate()
}
let timeNow = timeNowString() as String
for item in timeNow {
time = timeNow.componentsSeparatedByString(":")
}
}
var time = [String]()
var startTime = NSTimeInterval()
override func viewDidLoad() {
super.viewDidLoad()
}
func countTime() {
var currentTime = NSDate.timeIntervalSinceReferenceDate()
var elapsedTime: NSTimeInterval = currentTime - startTime
var adjustedTime = Int(elapsedTime) + 3600*time[0].toInt()! + 60*time[1].toInt()! + time[0].toInt()!
var hours = Int(Double(adjustedTime)/3600.0)
let minutes = Int(Double(adjustedTime - hours*3600)/60.0)
let seconds = adjustedTime - hours*3600 - minutes*60
let startHours = hours > 9 ? String(hours):"0" + String(hours)
let startMinutes = minutes > 9 ? String(minutes):"0" + String(minutes)
let startSeconds = seconds > 9 ? String(seconds):"0" + String(seconds)
timeLabel.text = "\(startHours):\(startMinutes):\(startSeconds)"
}
func timeNowString() -> NSString {
let date = NSDate()
var outputFormat = NSDateFormatter()
outputFormat.locale = NSLocale(localeIdentifier:"en_US")
outputFormat.dateFormat = "HH:mm:ss"
let timeString = outputFormat.stringFromDate(date)
return timeString;
}
}