Sending data to another view controller - ios

This is a learning app that I am making for fun, I have been stuck here for 2 days.
I have two views setup that I use to send data that the user will pick to the other one (they are named AddCoinVC and MainVC).
In AddCoinVC, the sending is performed when the user clicks on the button
let vc = MainViewController()
vc.coinArray.append(CoinWallet(coinName: "Test", coinSymbol: "Test", coinAmount: "0"))
performSegue(withIdentifier: "backToMain", sender: self)
I have setup a breakpoint at this point and printing vc.coinArray prints me the correct value =
($R0 = 1 value { (coinName = "Test", coinSymbol = "Test", coinAmount = "0")
}
But when I go to my other breakpoint at MainVC, it displays 0 value.
var coinArray = [CoinWallet]()
This is the var that I use, the default in MainVC is CoinWallet which is empty when first loading the app. This is the custom Class.
class CoinWallet {
var coinName:String = ""
var coinSymbol:String = ""
var coinAmount:String = ""
init(coinName: String, coinSymbol:String, coinAmount: String) {
self.coinName = coinName
self.coinSymbol = coinSymbol
self.coinAmount = coinAmount
}
}
When the segue and the sending is performed from AddCoinVC to MainVC 'coinArray' should have this value sent to it.
Why would be the value empty if vc.coinArray has 1 value?

The error occurs because MainViewController() does not return the view controller you expect. It's a new blank instance which is not the instance in the storyboard.
Since you are performing a segue anyway, pass the CoinWallet instance as sender parameter in performSegue
let coin = CoinWallet(coinName: "Test", coinSymbol: "Test", coinAmount: "0")
performSegue(withIdentifier: "backToMain", sender: coin)
Then implement prepare(for segue and use the destination property as reference to the main view controller.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "backToMain" {
let coin = sender as! CoinWallet
let mainViewController = segue.destination as! MainViewController
mainViewController.coinArray.append(coin)
}
}

You should add this method in your AddCoinVC and send data as below,
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let mainVC = segue.destination as? MainViewController {
mainVC.coinArray.append(CoinWallet(coinName: "Test", coinSymbol: "Test", coinAmount: "0"))
}
}
In the below lines of code, you are just creating a new instance that is not the viewController being segued.
let vc = MainViewController()
vc.coinArray.append(CoinWallet(coinName: "Test", coinSymbol: "Test", coinAmount: "0"))
You should only perform the segue on button click as below and set any data inside the above method.
performSegue(withIdentifier: "backToMain", sender: self)

Related

How to access custom annotation properties when using calloutAccessoryView as the sender of a segue to a new viewcontroller?

I have the following code to prepare for my segue:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Make sure we are acting on the correct segue
if segue.identifier == "CreateJumpSpot", let jumpSpotCreatorControllerVC = segue.destination as? JumpSpotCreatorController {
// Set the delegate in the JumpSpotCreatorController we're navigating to
jumpSpotCreatorControllerVC.delegate = self
} else if segue.identifier == "JumpSpotInfo", let jumpSpotInfoVC = segue.destination as? JumpSpotInfoController {
if let senderAnnotationView = sender as? JumpSpotAnnotationView {
jumpSpotInfoVC.titleLabel.text = senderAnnotationView.annotation?.title as? String
jumpSpotInfoVC.imageView.image = senderAnnotationView.annotation.
}
}
}
We are focusing on the 'else if' part of the statement here. I have a custom annotation and annotation view. I am populating labels and imageViews in the view controller that I am segueing to, using the properties of the annotation that the user clicked on to reveal the .detailDisclosure version of the rightCalloutAccessoryView. However that sender (.detailDisclosure of rightCalloutAccessoryView) is only allowing me to access the title and subtitle of the annotation. As you can see when I got to the image property I stopped typing, as there was no property to access. How can I access the properties of my custom annotation?
Can‘t you just get the image by senderAnnotationView.annotation?.image, just like what you are doing to get the title?
PS: Don't depend too much on Xcode autocompletion. Sometimes it's doesn't work perfectly well.
Ok I figured it out. All I had to do was adjust the code so that I had a constant of the annotation itself, and cast it as my custom class. Here's the code:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Make sure we are acting on the correct segue
if segue.identifier == "CreateJumpSpot", let jumpSpotCreatorControllerVC = segue.destination as? JumpSpotCreatorController {
// Set the delegate in the JumpSpotCreatorController we're navigating to
jumpSpotCreatorControllerVC.delegate = self
} else if segue.identifier == "JumpSpotInfo", let jumpSpotInfoVC = segue.destination as? JumpSpotInfoController {
if let senderAnnotationView = sender as? JumpSpotAnnotationView {
let senderAnnotation = senderAnnotationView.annotation as? JumpSpotAnnotation
jumpSpotInfoVC.titleLabel.text = senderAnnotation?.title
jumpSpotInfoVC.imageView.image = senderAnnotation?.image
jumpSpotInfoVC.descriptionLabel.text = senderAnnotation?.description
jumpSpotInfoVC.heightLabel.text = senderAnnotation?.estimatedHeight
jumpSpotInfoVC.warningsLabel.text = senderAnnotation?.warnings
}
}
}
The key line there is: let senderAnnotation = senderAnnotationView.annotation as? JumpSpotAnnotation

transfer weather data to container view

Im unable to update labels in container view. Here's how i've done it.
​
I wrote my updateWeather function in main VC and retrieved the weather data successfully. when i printed weatherJSON it shows all the received data in console.
now when i started writing updateUI function i could only update the labels on main VC.
so i used prepare segue to send data to container view and sent a string to container VC and updated "humidity" label successfully. all labels accept strings without any issues.
but i have no idea how to send weather data to container view.
i tried passing values using object weatherDataModel but nothing happens. i even declared a new object referring to container view class and used it in updateUI function to set label values but it won't work too.
I have no idea what to pass in place of string to get weather data through to next VC.
override func prepare(for segue: UIStoryboardSegue, sender for: Any?) {
if segue.identifier == "displayFullWeatherInfo"{
let destinationVC = segue.destination as! FullWeatherViewController
destinationVC.delegate = "\(weatherDataModel.pressure)"
....
....
Heres my WeatherDataModel Class:
import Foundation
class WeatherDataModel{
var city = ""
var temp = 0
var country = ""
var humidity = 0
}
in my main VC i have created weatherDataModel object and here's my updateWeatherInfo code:
func updateWeatherInfo(json : JSON){
if let tempDefault = json["data"][0]["temp"].double{
weatherDataModel.temp = Int(tempDefault)
weatherDataModel.city = json["data"][0["city_name"].stringValue
weatherDataModel.country = json["data"][0]["country_code"].stringValue
weatherDataModel.humidity = json["data"][0]["rh"].intValue
updateWeatherUI()
}
else{
currentLocation.text = "Not Available"
}
}
​
Create a property for your weather model object in your second VC FullWeatherViewController:
var weatherDataModel: WeatherDataModel! //your object
And in your first VC:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "displayFullWeatherInfo" {
let destinationVC = segue.destination as! FullWeatherViewController
destinationVC.weatherDataModel = weatherDataModel
}

Assign strings to textfields in another view controller

I am trying to take facebook values, such as name and email, and transferring them to another view controller where they populate their corresponding text fields. However, when I try to populate the text fields with this code
let fbEmail = data["email"]
let fbName = data["name"]
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let vc = segue.destination as? CreateAccountVC
vc!.emailTxt = self.fbName
vc!.fullnameTxt = self.fbEmail
}
I receive an error saying "Cannot assign type String to type UITextField!" I'm confused here as I thought that text fields only take strings (I'm very new to coding/programming).
So basically, how do I get these values gathered in one view controller to populate text fields in another view controller? Any help would be greatly appreciated! Thanks!
Error reason is you need to assign it to the UITextField's text property.
You can also use variables to set them. And set textfield's values in your CreateAccountVC controller's viewDidLoad function. Like this:
Your first controller where you take facebook values:
let fbEmail = data["email"]
let fbName = data["name"]
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let vc = segue.destination as? CreateAccountVC {
vc.emailTxtValue = self.fbName
vc.fullnameTxtValue = self.fbEmail
}
}
Your CreateAccountVC :
class CreateAccountVC: UIViewController {
var emailTxtValue:String?
var fullnameTxtValue:String?
override func viewDidLoad() {
super.viewDidLoad()
emailTxt.text = emailTxtValue
fullnameTxt.text = fullnameTxtValue
}
}

Swift - Passing data coming from an API to another view controller

Please check the below code:
#IBAction func sendActivationCode(_ sender: UIButton) {
service.Register(phoneNumber: self.mobileNumberTxt.text!, callback: { (response) in
self.setCustomerValues(response: response)
})
}
func setCustomerValues(response: [String:Any]) {
registrationToken = (response["token"]! as! String)
code = response["code"] as! Int
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toStep2" {
let vc = segue.destination as! Step2ViewController
vc.registrationToken = registrationToken
}
}
The problem is: prepare function is executed before setCustomerValues and I cannot use registrationToken variable in Step2ViewController.swift because it's nil.
Instead of connecting your segue from the button to Step2ViewController, connect it from the view controller. This way the segue will not automatically be performed when the button is touched.
Then call performSegue from within your setCustomerValues callback to perform the segue explicitly after getting the registration token. Note that if the callback is not on the main thread, you will need to dispatch_async to the main thread before calling performSegue.
You should push viewcontroller after self.setCustomerValues(response: response). Don't push viewcontroller when sendActivationCode
The best way to come out of this problem is to create an IBAction method from your button on a Touch Up Inside Event and not create any Segues on 'action' of your button.
Use the following code:
#IBAction func sendActivationCode(_ sender: UIButton) {
service.Register(phoneNumber: self.mobileNumberTxt.text!, callback: {
(response) in
let vc = self.storyboard?.instantiateViewControllerWithIdentifier("Step2ViewController") as! Step2ViewController
vc.registrationToken = (response["token"]! as! String)
vc.code = response["code"] as! Int
self.navigationController?.pushViewController(vc!, animated: true)
})
}

Passing data between view controllers through segue

I have a MapViewController with a prepareForSegue(_:sender:)method, which I intend to use to send data to LandmarkTableViewController, and is called when a button is pressed.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let destinationvc = segue.destinationViewController
if let landmarkvc = destinationvc as? LandmarkTableViewController {
if let identifier = segue.identifier {
let library = Landmark(name: "Run Run Shaw Library", properties: ["Chinese Kanji", "Gray", "Green Plants"])
let bank = Landmark(name: "Hang Seng Bank", properties: ["Chinese Kanji", "Green"])
switch identifier {
case "showLibrary" : landmarkvc.passedLandmark = library // pass data to LandmarkTableViewController
case "showBank" : landmarkvc.passedLandmark = bank // pass data to LandmarkTableViewController
default : break
}
}
}
}
The LandmarkTableViewController is properly set up to display the String array properties, with one String on each row. So what I intend to do is pass the appropriate data for the table to properties according to which button was pressed, and let LandmarkTableViewController display the corresponding properties.
class LandmarkTableViewController: UITableViewController {
var properties = [String]()
var passedLandmark = Landmark(name: "temp", properties: ["temp"]) // initially set to default value
override func viewDidLoad() {
super.viewDidLoad()
loadSampleProperties()
}
func loadSampleProperties() {
self.properties = passedLandmark!.properties
}
// other methods....
}
class Landmark {
var name: String
var properties: [String]
init?(name: String, properties: [String]) {
self.name = name
self.properties = properties
// Initialization should fail if there is no name or if there is no property.
if name.isEmpty || properties.isEmpty {
return nil
}
}
However, when I run the code, only temp is displayed in the table view. I've been stuck on this for a long time now, so any help is much appreciated!
Edit: loadData() inside of viewDidLoad() is changed to the correct loadSampleProperties(). I made an error while posting the code to the question.
I think this should solve your problem if not double check your identifiers
and you can make sure to data passing with adding print(passedLandmark) to viewDidLoad() or breakpoint to make sure you getting the data
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let destinationvc = segue.destinationViewController
if let landmarkvc = destinationvc as? LandmarkTableViewController {
if segue.identifier == "showLibrary" {
let library = Landmark(name: "Run Run Shaw Library", properties: ["Chinese Kanji", "Gray", "Green Plants"])
landmarkvc.passedLandmark = library
}
if segue.identifier == "showBank" {
let bank = Landmark(name: "Hang Seng Bank", properties: ["Chinese Kanji", "Green"])
landmarkvc.passedLandmark = bank
}
}
}
Hope this will helps
Code is missing from your quote, so I can't be sure, but I assume your loadData() method is the one that reloads the table view data with Landmark you've passed in prepareForSegue. If that is the case:
viewDidLoad() is called before prepareForSegue, so that all the views and elements of the destinationViewController are loaded and ready to use. Thus, in your case, the table view is loaded with your "temp" data and nothing makes it reload when you set the proper one.
You have two options:
You could call loadData()/reloadData() in viewWillAppear for example, which is called after prepareForSegue(). Bare in mind that viewWillAppear will possibly be called again in some other navigation.
Otherwise, you could instantiate and present/push the new controller in your parent view controller, instead of using the segue.

Resources