converting enum variable to anyObject - ios with swift - ios

let me explain my code and then state the problem i'm facing
i have two viewControllers classes
1- difficultyViewController : where the user chooses the difficulty of the game
**difficultyViewController has three buttons for the user to click on which difficulty is desired
2- gameViewController : where the game will be presented to the user
**currently in the gameViewController only have a label
in the difficultyViewController i have an enum which represent the three game difficulties
class difficultyViewController: UIViewController {
enum difficulties {
case Easy
case Medium
case Hard
}
var gameDifficulty : difficulties?
// other code is here
}
and in the gameViewController i have a variable correspond to this enum
class gameViewController: UIViewController {
#IBOutlet weak var gameDifficultyLabel: UILabel!
var gameDifficulty : difficultyViewController.difficulties?
// other code is here
}
in the difficultyViewController i'm using code to perform and prepare for the segue
#IBAction func easyButtonPressed(sender: AnyObject) {
gameDifficulty = .Easy
performSegueWithIdentifier("toGame", sender: gameDifficulty as? AnyObject)
}
and here is the prepare for segue code
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "toGame" {
if let gameVC = segue.destinationViewController as? gameViewController {
if let difficulty = sender as? difficulties {
print(difficulty)
gameVC.gameDifficulty = difficulty
}
}
}
}
and now the problem i'm facing is
when sending the difficulty as an argument to the perform segue, the conversion from the enum variable to the not valid and i always receive a nil value
what is the reason for that ? is it not possible to convert an enum to anyObject ?

You are setting the game difficulty variable when the user presses the button, so why not just set the difficulty level based on that value?
Also, your class names and enum names should be capitalized to differentiate them from variable names.
class DifficultyViewController: UIViewController {
enum Difficulties {
case Easy
case Medium
case Hard
}
var gameDifficulty : Difficulties?
// other code is here
}
class GameViewController: UIViewController {
#IBOutlet weak var gameDifficultyLabel: UILabel!
var gameDifficulty : DifficultyViewController.Difficulties?
// other code is here
}
#IBAction func easyButtonPressed(sender: AnyObject) {
gameDifficulty = .Easy
performSegueWithIdentifier("toGame", sender: AnyObject)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "toGame" {
if let gameVC = segue.destinationViewController as? gameViewController {
gameVC.gameDifficulty = gameDifficulty // You changed this in the IBAction, so simply send it on to the next VC
}
}
}
}

Related

Fatal error when trying to pass data to another view controller

In order to practice my networking, I built an app with a text field where you can input something. I use the wikipedia API to fetch the definition of that term / name/ expression. My goal is to then display that definition into another view controller.
A button performs the segue to the new view controller, where a label displays that definition.
The get request works, but when tapping the button, I get a fatalError : "Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value".
I would like to add that the error is displayed in the "prepare for segue" function.
Here is the code for my first view controller
import UIKit
import Alamofire
import SwiftyJSON
class ViewController: UIViewController {
#IBOutlet weak var textEntryLabel: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
//MARK: - Relevant variables
let wikipediaURl = "https://en.wikipedia.org/w/api.php"
var termDefinitionInfo: String = ""
let segueName: String = "toDefinition"
#IBAction func buttonToDefinition(_ sender: UIButton) {
// on fait la requete ici
httpCall(termDefinition: textEntryLabel.text ?? "nothing to pass")
performSegue(withIdentifier: segueName , sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == segueName {
let secondVC = segue.destination as! DefinitionViewController
secondVC.definitionLabel.text = termDefinitionInfo
}
}
//MARK: - NETWORKING
func httpCall(termDefinition: String) {
let parameters : [String:String] = [
"format" : "json",
"action" : "query",
"prop" : "extracts",
"exintro" : "",
"explaintext" : "",
"titles" : termDefinition,
"indexpageids" : "",
"redirects" : "1",
]
//
request(wikipediaURl, method: .get, parameters: parameters).responseJSON { (response) in
if response.result.isSuccess {
//1. on affiche le tableau json initial
let definitionJSON: JSON = JSON(response.result.value)
print(definitionJSON)
// deux valeurs : pageID et definition
let pageId = definitionJSON["query"]["pageids"][0].stringValue
let pageDefinition = definitionJSON["query"]["pages"][pageId]["extract"].stringValue
self.termDefinitionInfo = pageDefinition
print(self.termDefinitionInfo)
} else {
print("Error! Could not fetch data!")
}
}
}
}
Here is the code for the second view controller
import SwiftyJSON
import Alamofire
class DefinitionViewController: UIViewController {
#IBOutlet weak var definitionLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}```
Tip: Try to avoid force down casting
In your case you are trying to assign a value to an IBOutlet when it's not wired to its parent view controller. You better do this:
class DefinitionViewController: UIViewController {
#IBOutlet weak var definitionLabel: UILabel!
var labelValue: String?
override func viewDidLoad() {
super.viewDidLoad()
definitionLabel.text = labelValue
}
}
And in your first view:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == segueName {
if let secondVC = segue.destination as? DefinitionViewController {
secondVC.labelValue = termDefinitionInfo
}
}
}

Issue in Delegate while passing data back to ViewController

I'm having a problem passing array data back from one view controller ("VC2") to another ("VC1"). I do everything by the rules. I made a proper protocol in VC1.
But unfortunately I could not get the data back.
This is my code:
VC2
protocol RecivedData {
func dataRecived(nameArray: [String] , priceArray: [String])
}
var popUpdelegate : RecivedData?
#IBAction func nextBtnTapped(_ sender: UIButton) {
print("Hello")
let namedata = itemNameArr
let namePrice = itemPriceArr
self.popUpdelegate?.dataRecived(nameArray: namedata, priceArray: namePrice)
print(namedata)
print(namePrice)
self.view.removeFromSuperview()
}
VC1
class HomeVC: UIViewController , RecivedData {
func dataRecived(nameArray: [String], priceArray: [String]) {
itemNameArr += nameArray
itemPriceArr += priceArray
print(itemNameArr, itemPriceArr)
print ("This is HomeVC")
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "sendSegue"{
let secondVC: AddOnItemPopUpVC = segue.destination as! AddOnItemPopUpVC
secondVC.popUpdelegate = self
}
}
}
Replace your code with this
protocol RecivedData : class {
func dataRecived(nameArray: [String] , priceArray: [String])
}
And
weak var popUpdelegate : RecivedData?
Now it will start working.
Make sure there will be no typo in segue name.

Can't get the value of a var in another class

I'm trying to get the value of a String var from an another class, but when i'm using it on the new class, the value is empty.
I've got the MainViewController.swift class with :
var movieIDSelected = String()
#IBAction func tapPosterButton(_ sender: UIButton) {
switch sender.tag
{
case 101: movieIDSelected = theaterMovieID[0]
print(movieIDSelected) //The value isn't empty
break
}
}
And the second MovieViewController.swift class with :
var HomeView = ViewPop()
override func viewDidLoad() {
super.viewDidLoad()
let movieID = HomeView.movieIDSelected
print(movieID) //The value is empty
}
With your current code try this in MainVC
if let home = UIApplication.shared.keyWindow?.rootViewController as? ViewPop {
print("home exists ",home.movieIDSelected)
}
//
but you should have only 1 segue to the destinationVC and link it to the VC not a segue for every button , then implement prepareForSegue and fire performSegue inside the button action to make the segue
//
#IBAction func tapPosterButton(_ sender: UIButton) {
switch sender.tag
{
case 101: movieIDSelected = theaterMovieID[0]
print(movieIDSelected) //The value isn't empty
self.performSegue(withIdentifier: "goToNext", sender:1)
break
}
}
//
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let des = segue.destination as! MovieViewController
des.sendedValue = self.movieIDSelected
des.buttonNumber = sender as! Int
}
//
class MovieViewController : UIViewController {
var sendedValue = ""
var buttonNumber = 0 // default value won't affect
}

Swift passing calculated data back to previous view controller

I am creating my first simple budgeting app. Basically, I take a few user inputs like monthly income & savings goal. Then they click "start", & the app calculates stuff such as, their daily budget etc.
Here I'm running into trouble. After all the calculations, I display "how much you can spend each day" (e.g. $20 a day), which I pass forward through segues from their original inputs on the original screen.
Now, in this VC (UserInfoVC) I created a button which lets them add how much money they spent today. So when they click this "add money spent" button, I open a new VC (AddSubtractMoney) where I present a calculator where they can enter how much they spent today (i.e. $12) and click submit.
I run their input compared to their daily budget to get a New daily budget.
Now, I'm having trouble passing this updated number backwards, to display it on the previous VC on the label "dailySpendingLimitLabel". I know segues are not the best way to go about passing data backwards.
I've tried closures, but I end up getting lost in the syntax, and protocols and delegates (it's my 2nd month coding).
Is there a simple way to achieve passing this data back to the previous VC and populating the data in that previous display label?
Below is the code.
The First snippet is from the UserInfoVC where I display their originally entered data that I segued through. The Second snippet is from the AddSubtractMoney class where I placed the calculator and created an object "newestUpdate" inside a function that allows me to calculate the number they entered on the calculator minus their old daily budget. To arrive at a new budget which I want to present backwards to the UserInfoVC.
class UserInfoViewController : ViewController {
var userNamePassedOver : String?
var userDailyBudgetPassedOver : Double = 99.0
var userDailySavingsPassedOver : Double = 778.00
var userMonthlyEarningsPassedOver : Double?
var userDesiredSavingsPassedOver : Double?
var newAmountPassedBack : Double = 0.0
#IBOutlet weak var dailySavingsNumberLabel: UILabel!
#IBOutlet weak var userNameLabel: UILabel!
#IBOutlet weak var dailySpendingLimitLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
userNameLabel.text = userNamePassedOver
dailySpendingLimitLabel.text = String(format: "%.2f", userDailyBudgetPassedOver)
dailySavingsNumberLabel.text = String(format: "%.2f", userDailySavingsPassedOver)
}
#IBAction func addSubtractMoneyPressed(_ sender: UIButton) {
performSegue(withIdentifier: "addOrSubtractMoney", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "addOrSubtractMoney"{
let addOrSubtractMoneyVC = segue.destination as! AddSubtractMoney
addOrSubtractMoneyVC.dailyBudgetPassedThrough = userDailyBudgetPassedOver
}
}
}
extension UserInfoViewController: AddSubtractMoneyDelegate {
func calculatedValue(value: Double) {
dailySpendingLimitLabel.text = String(userDailyBudgetPassedOver - value)
}
}
import UIKit
protocol AddSubtractMoneyDelegate {
func calculatedValue(value: Double)
}
class AddSubtractMoney: UIViewController {
#IBOutlet weak var outputLabel: UILabel!
var runningNumber = ""
var finalNumberPassedOver : Double?
var amountPassedBackToUserInfo : Double = 0.0
var dailyBudgetPassedThrough : Double = 0.0
var delegate: AddSubtractMoneyDelegate?
override func viewDidLoad() {
super.viewDidLoad()
outputLabel.text = "0"
// Do any additional setup after loading the view.
}
#IBAction func buttonPressed(_ sender: UIButton) {
runningNumber += "\(sender.tag)"
outputLabel.text = runningNumber
}
#IBAction func submitNewInfo(_ sender: UIButton) {
// FIX FIX
AddSubtractMoneyController.addToMoneySpentArray(amountISpent: outputLabel.text!)
sendBackUpdatedNumber()
dismiss(animated: true, completion: nil)
}
#IBAction func allClearedPressed(_ sender: UIButton) {
runningNumber = ""
outputLabel.text = "0"
}
// THIS LINE PRODUCES THE CORRECT INPUT IN OUTPUT CONSOLE WHEN I PRINT- BUT I CANT FIGURE HOW TO TRANSFER IT BACK TO PREVIOUS VC
func sendBackUpdatedNumber(){
let newestUpdate = UserInfo(whatYouSpentToday: runningNumber, oldDailyBudgetPassed: dailyBudgetPassedThrough)
amountPassedBackToUserInfo = dailyBudgetPassedThrough - Double(runningNumber)!
newestUpdate.goalToSaveDaily = amountPassedBackToUserInfo
print(amountPassedBackToUserInfo)
self.delegate?.calculatedValue(value: amountPassedBackToUserInfo)
}
}
My suggestion is to use a callback closure. It's less code and easier to handle than protocol / delegate.
In AddSubtractMoney declare a callback variable and call it in sendBackUpdatedNumber passing the Double value
class AddSubtractMoney: UIViewController {
// ...
var callback : ((Double)->())?
// ...
func sendBackUpdatedNumber(){
let newestUpdate = UserInfo(whatYouSpentToday: runningNumber, oldDailyBudgetPassed: dailyBudgetPassedThrough)
amountPassedBackToUserInfo = dailyBudgetPassedThrough - Double(runningNumber)!
newestUpdate.goalToSaveDaily = amountPassedBackToUserInfo
print(amountPassedBackToUserInfo)
callback?(amountPassedBackToUserInfo)
}
}
In prepare(for segue assign the closure to the callback variable and add the code to be executed on return
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "addOrSubtractMoney"{
let addOrSubtractMoneyVC = segue.destination as! AddSubtractMoney
addOrSubtractMoneyVC.callback = { result in
print(result)
// do something with the result
}
addOrSubtractMoneyVC.dailyBudgetPassedThrough = userDailyBudgetPassedOver
}
}
Using delegate
if segue.identifier == "addOrSubtractMoney" {
let addOrSubtractMoneyVC = segue.destination as! AddSubtractMoney
addOrSubtractMoneyVC.dailyBudgetPassedThrough = userDailyBudgetPassedOver
addOrSubtractMoneyVC.delegate = self
}
}
You need to add delegate property in AddSubtractMoney class
var delegate: AddSubtractMoneyDelegate?
Create Protocol in AddSubtractMoney class
protocol AddSubtractMoneyDelegate {
func calculatedValue(value: Double)
}
And respond to delegate
func sendBackUpdatedNumber(){
let newestUpdate = UserInfo(whatYouSpentToday: runningNumber, oldDailyBudgetPassed: dailyBudgetPassedThrough)
amountPassedBackToUserInfo = dailyBudgetPassedThrough - Double(runningNumber)!
newestUpdate.goalToSaveDaily = amountPassedBackToUserInfo
print(amountPassedBackToUserInfo)
self.delegate.calculatedValue(value: amountPassedBackToUserInfo)
}
Now you need to implement this delegate method in class where delegate is set.
Here in UserInfoViewController class delegate is set so you need to implement its delegate method
extension UserInfoViewController: AddSubtractMoneyDelegate {
func calculatedValue(value: Double) {
//set label here
}
}
You could possibly also use an unwind segue to pass back the data.
If you don't under stand flow behind delegate(protocol oriented), you can simply go through below code. it only works if both class
But it is not a good practice
Learn about protocol, closure, or Notification Center broadcasting for mostly used, flexible and reusable coding methods.
UserInfoViewController
class UserInfoViewController : ViewController {
fun receiveBackUpdatedNumber(numberString:String){
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "addOrSubtractMoney"{
let addOrSubtractMoneyVC = segue.destination as! AddSubtractMoney
addOrSubtractMoneyVC.userInfoViewController = self
}
}
}
}
AddSubtractMoney
class AddSubtractMoney: UIViewController {
var userInfoViewController: UserInfoViewController!
var updatedNumber = ""
func sendBackUpdatedNumber(){
self.userInfoViewController.receiveBackUpdatedNumber(numberString: updatedNumber)
}
}
If you are confirtable you can go with protocols.. protocols insist a class to compulsory implement a method, which make code more reusable and independent.
In Above method we are passing instance of current viewcontroller(UserInfoViewController) to next viewcontroller(AddSubtractMoney) on performing segue, So by that we can access any properties of function in UserInfoViewController from AddSubtractMoney. So it make easy to pass data from AddSubtractMoney to -> UserInfoViewController

How can I call a method that is inside a UIViewController embedded in a container from a parent UIViewController?

I have an ios app in swift and I have a UIViewController (let's call it parentController) with a container. This container embeds another UIViewController called embedController.
embedController contains a method that prints a message to a console.
How can I call this method from my parentController?
I tried to use protocols, my current code is as follows:
class ParentController: UIViewController {
var handleEmbedController:HandleEmbedController?
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "embedViewSegue"){
if let embed = segue.destinationViewController as? EmbedController {
embed.value1 = value1
}
}
#IBAction func sendMsgButtonAction(sender: AnyObject) {
handleEmbedController?.printMsg() //this so far does nothing
}
}
and my embedController:
protocol HandleEmbedController: class {
func printMsg()
}
class EmbedController: UITableViewController, HandleEmbedController{
var value1 = ""
func printMsg(){
print("printing some embedded message")
}
}
How can I print this message from a parent controller?
What are you doing in your prepare for segue? Aren't you supposed to set your delegate (protocol) there? Like this:
if (segue.identifier == "embedViewSegue"){
if let embed = segue.destinationViewController as? EmbedController {
self.handleEmbedController = embed
}
}
If you put a breakpoint in sendMsgButtonAction you should see that the property handleEmbedController is nil. And that's why the method call does nothing since you are safely unwrapping it with ?.

Resources