Cannot Pass Data between TableViewControllers - ios

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

Related

How do I get a value from a variable in another View Controller

I'm trying to get the values from 4 different variables in PrefsViewController in ViewController but when PrefsViewController is dismissed the values reset.
Snippet of ViewController.swift
class ViewController: NSViewController, NSWindowDelegate {
var triesEnabled = false
var minNumber = 0
var maxNumber = 20
var maxTries = 3
#objc func applyPrefs() {
let mainSB = NSStoryboard(name: "Main", bundle: nil)
let prefsVC: PrefsViewController = mainSB.instantiateController(withIdentifier: "prefsViewController") as! PrefsViewController
minNumber = prefsVC.prefsMinNum
maxNumber = prefsVC.prefsMaxNum
triesEnabled = prefsVC.prefsTriesEnabled
maxTries = prefsVC.prefsMaxTries
print("\(minNumber) \(maxNumber) \(triesEnabled) \(maxTries)") // here I can see that it has been reset to the default values
resetGame(minRange: minNumber, maxRange: maxNumber, isTriesEnabled: triesEnabled, triesLimit: maxTries)
}
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(applyPrefs), name: Notification.Name("notifyApplyPrefs"), object: nil )
resetGame(minRange: minNumber, maxRange: maxNumber, isTriesEnabled: triesEnabled, triesLimit: maxTries)
}
}
Snippet of PrefsViewController
class PrefsViewController: NSViewController {
var triesOn = false
var prefsMinNum: Int = 0
var prefsMaxNum: Int = 20
var prefsTriesEnabled: Bool = false
var prefsMaxTries: Int = 3
#IBAction func closeButton(_ sender: Any) {
if (!minNumTextField.stringValue.isInt || !maxNumTextField.stringValue.isInt || !triesTextField.stringValue.isInt) {
let alert = NSAlert()
alert.messageText = "Error"
alert.informativeText = "You can only enter (whole) numbers in the text fields."
alert.alertStyle = .warning
alert.addButton(withTitle: "OK")
alert.runModal()
return
}
prefsMinNum = Int(minNumTextField.intValue)
prefsMaxNum = Int(maxNumTextField.intValue)
prefsTriesEnabled = triesOn
prefsMaxTries = Int(minNumTextField.intValue)
print("minNumTextField: \(prefsMinNum) maxNumTextField: \(prefsMaxNum) triesCheckBox: \(prefsTriesEnabled) triesTextField: \(prefsMaxTries)")
NotificationCenter.default.post(name: Notification.Name("notifyApplyPrefs"), object: nil)
self.dismiss(self)
}
}
The prefs view is shown as a sheet (I don't know if that's important)
When you use instantiateController you're creating a new, never before used, instance of the controller. If you want to have the data transferred using a notification, send self as the object when you call post and then use that to get your variables.
You would also need to change applyPrefs so that it takes a Notification parameter so that the notification object is available to you.

How to transfer variable from NSNumberFormatter to another ViewController?

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.

Switch tab bar include sending data

How can i change the tab bar? i know this post seems duplicate but i cant find any exist question that similar to me. Right now my current
selectedIndex = 0
so i want to make it go to tab number 3 which is
selectedIndex = 2
But i also want to send data from currentView to nextView. if i using push+selectedindex it will go to tab 3 but push the view from selectedindex = 0, and there is no data send to selectedIndex = 2
My current code
func redeemBtnPressed(_ sender: UIButton) {
let selectedRedeemBtnInfo = fixedGridInfo[sender.tag] as! Dictionary<String, AnyObject>
sender.showsTouchWhenHighlighted = true
let storyboard = UIStoryboard(name: "FlightExploration", bundle: nil)
let searchFlightVC = storyboard.instantiateViewController(withIdentifier: "SearchFlightVC") as! SearchFlightViewController
var newFlightType = String()
if "\(selectedRedeemBtnInfo["FlightType"]!)" == "Return" {
newFlightType = "Round"
} else {
newFlightType = "One"
}
searchFlightVC.flightType = newFlightType
searchFlightVC.fromHome = true
searchFlightVC.departure = "\(selectedRedeemBtnInfo["Departure"]!) (\(selectedRedeemBtnInfo["DepartureCityCode"]!)"
searchFlightVC.arrival = "\(selectedRedeemBtnInfo["Destination"]!) (\(selectedRedeemBtnInfo["DestinationCityCode"]!)"
self.navigationController?.pushViewController(searchFlightVC, animated: true)
tabBarController?.selectedIndex = 2
}
You can try this to send the data to that UIViewController which is a ViewController of UITabBarController
var yourViewController : TempViewController
if let arrController = tabBarController?.viewControllers {
for vc in arrController {
if vc is TempViewController {
yourViewController = vc as! TempViewController
}
}
}
yourViewController.yourData = dataToPass
tabBarController?.selectedIndex = 2
Modified from Rajat's answer seems helped me to solved the issue
func redeemBtnPressed(_ sender: UIButton) {
let selectedRedeemBtnInfo = fixedGridInfo[sender.tag] as! Dictionary<String, AnyObject>
sender.showsTouchWhenHighlighted = true
var newFlightType = String()
if "\(selectedRedeemBtnInfo["FlightType"]!)" == "Return" {
newFlightType = "Round"
} else {
newFlightType = "One"
}
if let arrController = tabBarController?.viewControllers {
for vc in arrController {
if vc.childViewControllers[0] is SearchFlightViewController {
let displayViewController = vc.childViewControllers[0] as! SearchFlightViewController
let _ = displayViewController.navigationController?.popToRootViewController(animated: true)
//displayViewController.flightType = newFlightType
displayViewController.flightTypeFromHome = newFlightType
displayViewController.fromHome = true
displayViewController.departure = "\(selectedRedeemBtnInfo["Departure"]!) (\(selectedRedeemBtnInfo["DepartureCityCode"]!)"
displayViewController.arrival = "\(selectedRedeemBtnInfo["Destination"]!) (\(selectedRedeemBtnInfo["DestinationCityCode"]!)"
displayViewController.flightType = "Round"
displayViewController.departureDateLbl = "Select One"
displayViewController.passenger = "1 Adult"
displayViewController.adultCount = 1
displayViewController.childCount = Int()
displayViewController.infantCount = Int()
tabBarController?.selectedIndex = 2
tabBarController?.tabBar((tabBarController?.tabBar)!, didSelect: (tabBarController?.tabBar.items?[2])!)
}
}
}
}
Rajat's answer Switch Tab Bar and Pass data, in Swift 4.2, iOS 11 need some changes to be done:
func switchToTab2(){}
var yourViewController = MyTab2ViewController()
if let arrController = self.tabBarController?.viewControllers {
for vc in arrController {
if vc is MyTab2ViewController {
yourViewController = vc as! MyTab2ViewController
yourViewController.productTitle = "Title"
self.tabBarController?.selectedIndex = 1 /// tabs start from 0
}
}
}
}
MyTab2ViewController is your viewcontroller connected to tab bar with index 1. (first tab index: 0)

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