How to transfer variable from NSNumberFormatter to another ViewController? - ios

I have a func with NSNumberFormatter:
#objc func doneClicked() {
view.endEditing(true)
let formatter = NumberFormatter()
formatter.locale = Locale.current
formatter.numberStyle = .decimal
formatter.minimumSignificantDigits = 6
if let text = textField.text, let number = formatter.number(from: text) {
year = number.doubleValue
mounthresult = year * 12
mounthLabel.text = formatter.string(from: NSDecimalNumber(value: year).multiplying(by: 12))
}
}
I need to process a data transfer to Label in another ViewController.
Making a prepare forSegue func:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "yearmounth" {
let mounthController = segue.destination as! MounthsViewController
mounthController.mounth = mounthresult
}
In this case transferred var haven't formatting. And shows without localization.
mounthresult = formatter.number(from: mounthLabel.text!) as! Double
This case gives nothing too... It's transferred but shows without formatter.
Also i tried to insert NumberFormatter to another controller's ViewDidLoad. But in this case NumberFormatter isn't load in ViewDidLoad...
Anyone knows the ways of solution?

You could simply pass formatted text:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "yearmounth" {
let mounthController = segue.destination as! MounthsViewController
mounthController.mounth = mounthLabel.text
}
Or store the number in a variable and pass it similar way.

Related

How to add two numbers in Swift only when both of the text fields are filled in

I'm trying to add two numbers in Swift 5, and I want to add some error checks. I don't want to make it possible for a user to click on the plus button if both of the text fields are not filled in. I tried with the if state below but it did not work.
the whole function:
#IBAction func sum(_ sender: Any) {
let one = input1.text
let oneInt = Int(one!)
let two = input2.text
let twoInt = Int(two!)
let total = oneInt! + twoInt!
label.text = "\(total)"
if(input2.text == nil){
addBtn.isEnabled = false
}
if(input1.text == nil){
addBtn.isEnabled = false
}
}
Try to use guard like this. If your input field does not contain any value that field return blank string and when you try to get integer value from that string it will return nil and your add button will be disable.
#IBAction func sum(_ sender: Any) {
guard let text1 = input1.text, let intValue1 = Int(text1) else {
addBtn.isEnabled = false
return
}
guard let text2 = input2.text, let intValue2 = Int(text2) else {
addBtn.isEnabled = false
return
}
label.text = "\(intValue1 + intValue2)"
}
A nice and simple way is to addTarget to your textFiels. This will enable you to handle the events on the text field. In this scenario we'll use .editingChanged and use a single selector to achieve our goal:
What we'll do : We will listen for when someone types something in the textfield. Whenever a text changed was made, we'll check to see if all the textfields was populated and then if it was we enable the sum button.
A small controller sample :: Make sure to read the comments to understand the code faster
import UIKit
class ViewController: UIViewController {
#IBOutlet var textfield1: UITextField!
#IBOutlet var textfield2: UITextField!
#IBOutlet var sumButton: UIButton!
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
sumButton.isEnabled = false /// Disable the button first thing
[textfield1, textfield2].forEach {
$0.addTarget(self, action: #selector(editingChanged(_:)), for: .editingChanged) /// add targets to handle the events (in your case it listens for the 'editingChanged' event )
}
}
#objc func editingChanged(_ textField: UITextField) {
/// Here we just loop through all our textfields
for each in [textfield1, textfield2] {
if let text = each?.text { /// Just make sure the textfields text is not nil
if text.count < 1 {
// If the textfiels text has no value in, then we keep the button disabled and return
sumButton.isEnabled = false
return
}
} else {
/// Else if the text field's text is nill, then return and keep the button disabled
sumButton.isEnabled = false
return
}
}
sumButton.isEnabled = true /// If the code reaches this point, it means the textfields passed all out checks and the button can be enabled
}
#IBAction func sum(_ sender: Any) {
let one = textfield1.text!
let two = textfield2.text!
guard let oneInt = Int(one), let twoInt = Int(two) else {
print("Whatever was in that text fields, couldn't be converted to an Int")
label.text = "Be sure to add numbers."
return
}
let total = oneInt + twoInt
label.text = "\(total)"
}
}
textfields are not nil but empty strings. so make your comparison like :
if input1.text == "" {
// do your check here
}
Seems like you want to start with addBtn.isEnabled = false then update it whenever the user enters two valid integers into the text fields, i.e. Int(input1.text ?? "") != nil && Int(input2.text ?? "") != nil. You can do this by adding a target to your textfields (input1 and input2) for .editingChanged events. For example, if you're doing this in a UIViewController, you can do this in viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
addBtn.isEnabled = false
input1.addTarget(self, action: #selector(textFieldDidEdit(_:)), for: .editingChanged)
input2.addTarget(self, action: #selector(textFieldDidEdit(_:)), for: .editingChanged)
}
Where textFieldDidEdit(_:) action looks like:
#objc func textFieldDidEdit(_ sender: UITextField) {
addBtn.isEnabled = Int(input1.text ?? "") != nil && Int(input2.text ?? "") != nil
}
Finally your sum function becomes:
#IBAction func sum(_ sender: UIButton) {
guard let oneInt = Int(input1.text ?? ""), let twoInt = Int(input2.text ?? "") else {
return
}
let total = oneInt + twoInt
label.text = "\(total)"
}
As all of the number validation has moved to the textFieldDidEdit(_:) function.

Cannot Pass Data between TableViewControllers

Merry Christmas! I am currently making a "reminders" app at the moment. My "secondary" TableViews fail to pass data back into the "main" TableView. (please see image below). I need the data to pass back into the "main" TableView so that I can save it and display that into another TableView, so the user can see the tasks they have put in.
The code inside the IconTableViewController (and ShowTableViewController):
var userImage: String = ""
var userTitle: String = ""
var userDesc: String = ""
var frequency: String = ""
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let navigationViewController = segue.destination as? UINavigationController {
guard let viewController = navigationViewController.topViewController as? NewItemTableViewController else { return }
let userImageNew = userImage
let userTitleOld = userTitle
let userDescOld = userDesc
let frequencyOld = frequency
viewController.selectedImage = userImageNew
viewController.userTitle = userTitleOld
viewController.userDesc = userDescOld
viewController.selectedFrequency = frequencyOld
}
}
The code inside NewItemTableViewController:
var selectedImage: String = ""
var selectedFrequency: String = ""
var userTitle: String = ""
var userDesc: String = ""
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showSelect" {
let userImageOld = selectedImage
let userTitleNew = titleTextField.text
let userDescNew = descTextView.text
let frequencyOld = selectedFrequency
let viewController = segue.destination as! ShowTableViewController
viewController.userImage = userImageOld
viewController.userTitle = userTitleNew!
viewController.userDesc = userDescNew!
viewController.frequency = frequencyOld
}
if segue.identifier == "iconSelect" {
let userImageOld = selectedImage
let userTitleNew = titleTextField.text
let userDescNew = descTextView.text
let frequencyOld = selectedFrequency
let viewController = segue.destination as! IconTableViewController
viewController.userImage = userImageOld
viewController.userTitle = userTitleNew!
viewController.userDesc = userDescNew!
viewController.frequency = frequencyOld
}
}
Thank you very much for your help!
Seems like NewItemTableViewControlleris not yet top viewController on the navigation stack. Try accessing it in the same trivial way,
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let viewController = segue.destination as? NewItemTableViewController {
let userImageNew = userImage
let userTitleOld = userTitle
let userDescOld = userDesc
let frequencyOld = frequency
viewController.selectedImage = userImageNew
viewController.userTitle = userTitleOld
viewController.userDesc = userDescOld
viewController.selectedFrequency = frequencyOld
}
}
You should put a breakpoint inside above method prepare(for segue and see if it is being called. Because i feel you don't have unwind segue as these viewControllers are just pushed on the navigation stack. So in that case you can also use viewWillDisappear to set the data by accessing NewItemTableViewController from the navigation stack. Something as below,
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if let vc = self.navigationController?.viewControllers.first(where: { $0 is NewItemTableViewController }) as? NewItemTableViewController {
// Set data.
}
}
You should use Delegate to push data back to NewItemTableViewController. You can refer this thread: Pass data back to previous viewcontrolle

How do I set up the buttons that are linked to didPressNumber to add to each other when pressed

How do I set up the buttons that are linked to didPressNumber to add to each other when pressed so lets say its a calculator and I want set it up where each button is pressed has a letter and number value when it is pressed it adds to the previous one press and I want to set up 2 labels one displaying the number value and one displaying the letter value and how would I set up the value of each number?
enum modes {
case not_set
case addition
case subtraction
case equals
}
#IBAction func didPressNumber(_ sender: UIButton) {
let stringValue:String? = sender.titleLabel?.text
if (lastButtonWasMode) {
lastButtonWasMode = false
labelString = "0"
}
labelString = labelString.appending(stringValue!)
updateText()
}
func updateText() {
guard let labelInt:Int = Int(labelString) else {
return
}
if (currentMode == .not_set) {
savedNum = labelInt
}
let formatter: NumberFormatter = NumberFormatter()
formatter.numberStyle = .decimal
let num:NSNumber = NSNumber(value: labelInt)
label.text = formatter.string(from: num)
}
func changeMode(newMode:modes) {
if (savedNum == 0) {
return
}
currentMode = newMode
lastButtonWasMode = true
}

When using prepareForSegue in Swift, it wont load data correctly the first time the cell is clicked

Using the functions below it will not load the picture or the text that is retrieved from the getMore function the first time the cell is clicked. If I click on the cell it will not load until I click the back button and the re-click on the cell.The get more function sets the emailAdress, birthday, address and the url for the picture.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var indexpath : NSIndexPath = self.tableView.indexPathForSelectedRow!
var destViewController = segue.destinationViewController as! SecondView
let row: Int = indexpath.row
getMore(row)
destViewController.nameString = namesArray[row]
destViewController.companyString = companyArray[row]
destViewController.homeNumber = homeNumber[row]
destViewController.workNumber = workNumber[row]
destViewController.mobileNumber = mobileNumber[row]
destViewController.emailAddress = self.email
if let url = NSURL(string: self.largeImageURL) {
if let data = NSData(contentsOfURL: url) {
let image1 = UIImage(data: data)
destViewController.pic = image1!
}
}
destViewController.birthday = birthDate[row]
destViewController.address = self.street + self.city + self.state + self.country + self.zip
}
I guess your function getMore loads data asynchronously, so the data may not be available when you set variables.
Sure you can pass a closure in getMore function where you will set all necessary variables and use this closure when data is fully loaded, but in this case you will block the UI. I would suggest to move function getData into SecondView and call it in viewDidLoad.
You need to take your image url loading off the main thread.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var indexpath : NSIndexPath = self.tableView.indexPathForSelectedRow!
var destViewController = segue.destinationViewController as! SecondView
let row: Int = indexpath.row
getMore(row)
destViewController.nameString = namesArray[row]
destViewController.companyString = companyArray[row]
destViewController.homeNumber = homeNumber[row]
destViewController.workNumber = workNumber[row]
destViewController.mobileNumber = mobileNumber[row]
destViewController.emailAddress = self.email
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
if let url = NSURL(string: self.largeImageURL) {
if let data = NSData(contentsOfURL: url) {
dispatch_async(dispatch_get_main_queue()) {
destViewController.pic = UIImage(data: data)
}
}
}
}
destViewController.birthday = birthDate[row]
destViewController.address = self.street + self.city + self.state + self.country + self.zip
}

Execute function on startup

I'm trying to develop an application for IOS using swift language that is a news for me. I want to fill a dictionary (tobaccoList) on the application startup. I have a csv file, so I take data from this file and than i fill the dictionary:
class DataManager{
var latitudes = Array<Double>()
var longitudes = Array<Double>()
var tobaccoList = Dictionary<Double, Tabacchino>()
init(){
if let url = NSURL(fileURLWithPath: "/Users/brunopistone/Developer/apptabacchi/LocationList_sorted.csv" , isDirectory: true) {
var error: NSErrorPointer = nil
if let csv = CSV(contentsOfURL: url, error: error) {
//put every tabbacchino in a Dictionary tobaccoList
let rows = csv.rows
let totalRows = rows.count
for var index = 1; index < totalRows; index++ {
let temp = csv.rows[index]
let tabacchino = Tabacchino(
name: temp["Name"]!, phone: temp["tnumber"]!, lat: NSString(string: temp["Latitude"]!).doubleValue, lon: NSString(string: temp["Longitude"]!).doubleValue
)
let keyGeo = NSString(string: temp["Latitude"]!).doubleValue
storeTobaccoShop(keyGeo, value: tabacchino)
var doubleLatitude = NSString(string: temp["Latitude"]!).doubleValue
var doubleLongitude = NSString(string: temp["Longitude"]!).doubleValue
storeLatitude(doubleLatitude)
storeLongitudes(doubleLongitude)
}
}
}
}
func storeTobaccoShop(key: Double, value: Tabacchino) {
self.tobaccoList[key] = value
}
In the viewController file of the home page i have:
class ViewController: UIViewController, CLLocationManagerDelegate {
let startFunction = DataManager()
let locationManager = CLLocationManager()
var latitude = Double()
var longitude = Double()
var tobaccoList = Dictionary<Double, Tabacchino>()
override func viewDidLoad() {
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
tobaccoList = startFunction.getTobaccoList()
}
In the home page, I have a button that calls another view, and i want to pass the dictionary to the other view in order to use it, so I use this method:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "tobaccoListSegue"{
let viewList = segue.destinationViewController as! ViewList
viewList.tabacchini = tobaccoList
}
}
The problem is, when i click on the button in order to call viewList, the application fills again the dictionary. What i want is to fill the dictionary only when I open the application.
Please help me fix this thing. Thanks
Put this line
let startFunction = DataManager()
Inside viewdidload() method.

Resources