Swift : Only transfers certain variable when using prepareforsegue function - swift

I am using the prepareforsegue function to transfer data between view controllers. I am attempting to transfer the data held by 5 variables yet only 4 transfer. I am wondering if it is a formatting issue considering the 4 that transfer correctly have different variable types to the other. Any help would be greatly appreciated. Thank you for your time.
Sender View Controller
import Foundation
import UIKit
class CratesViewController : UIViewController {
var unlockedAK47 = "false"
var unlockedDesertEagle = "false"
var unlockedGlock17 = "false"
var unlockedGlock18 = "false"
var coinsAmount = 0
let screenSize: CGRect = UIScreen.main.bounds
#IBOutlet weak var coins: UILabel!
#IBOutlet weak var cratesImageView: UIImageView!
#IBOutlet weak var unlockView: UIView!
#IBOutlet weak var backButtonOutlet: UIButton!
#IBOutlet weak var gunImageView: UIImageView!
#IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
coins.text = String(coinsAmount)
gunImageView.isHidden = true
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "cratesToMainSegue" {
let destViewController = segue.destination as! ViewController
destViewController.unlockedAK47 = unlockedAK47
destViewController.unlockedDesertEagle = unlockedDesertEagle
destViewController.unlockedGlock17 = unlockedGlock17
destViewController.unlockedGlock18 = unlockedGlock18
destViewController.coinsAmount = coinsAmount
}
}
}
Destination View Controller
import UIKit
class ViewController: UIViewController {
var unlockedAK47 = "false"
var unlockedDesertEagle = "false"
var unlockedGlock17 = "true"
var unlockedGlock18 = "false"
struct defaultsKeys {
static let keyOne = ""
}
var coinsAmount = 100
#IBOutlet weak var coinsAmountLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Getting
let defaults = UserDefaults.standard
if let stringOne = defaults.string(forKey: defaultsKeys.keyOne) {
coinsAmount = Int(stringOne)!
}
coinsAmountLabel.text = String(coinsAmount)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Note that the segue identifiers have been set in the interface builder
if segue.identifier == "cratesSegue" {
let destViewController = segue.destination as! CratesViewController
destViewController.coinsAmount = coinsAmount
destViewController.unlockedAK47 = unlockedAK47
destViewController.unlockedDesertEagle = unlockedDesertEagle
destViewController.unlockedGlock17 = unlockedGlock17
destViewController.unlockedGlock18 = unlockedGlock18
} else if segue.identifier == "collectionSegue" {
let destViewController = segue.destination as! CollectionViewController
destViewController.coinsAmount = coinsAmount
destViewController.unlockedAK47 = unlockedAK47
destViewController.unlockedDesertEagle = unlockedDesertEagle
destViewController.unlockedGlock17 = unlockedGlock17
destViewController.unlockedGlock18 = unlockedGlock18
}
}
#IBAction func saveButton(_ sender: Any) {
coinsAmount = coinsAmount + 10
coinsAmountLabel.text = String(coinsAmount)
// Saving
let defaults = UserDefaults.standard
defaults.set(String(coinsAmount), forKey: defaultsKeys.keyOne)
}
}
Please let me know if any more information is needed. :)

This is not because coinsAmount is a different type. This is because coinsAmount's value is overwritten in viewDidLoad:
let defaults = UserDefaults.standard
if let stringOne = defaults.string(forKey: defaultsKeys.keyOne) {
coinsAmount = Int(stringOne)! <<<< HERE!
}
I think what you want to do is to get the coins amount only if it is not passed to ViewController.
You should declare coinsAmount as an optional:
var coinsAmount: Int!
And check whether it is nil before overwriting it, so that you don't accidentally overwrite the passed value.
if coinsAmount == nil {
let defaults = UserDefaults.standard
if let stringOne = defaults.string(forKey: defaultsKeys.keyOne) {
coinsAmount = Int(stringOne)!
}
}

Related

Force unwrapping nil optional for UIImageView when transitioning to view controller

I'm running into an error when transitioning to view controllers by overriding the built-in prepare() function in Swift. I have a UIImageView for backgrounds on my screens. Here is the code for two of the view controllers in question.
import UIKit
import FirebaseAuth
class HomeVC: UIViewController {
#IBOutlet weak var signOutButton: UIButton!
#IBOutlet weak var backgroundImageView: UIImageView!
#IBOutlet weak var friendsNavButton: UIButton!
#IBOutlet weak var homeNavButton: UIButton!
#IBOutlet weak var profileNavButton: UIButton!
#IBOutlet weak var bumpButton: UIButton!
#IBOutlet weak var welcomeLabel: UILabel!
#IBOutlet weak var doNotDisturbLabel: UILabel!
#IBOutlet weak var doNotDisturbButton: UIButton!
var userName = ""
var dndIsOn: Bool = false
#IBAction func dndToggled(_ sender: Any) {
dndIsOn = !dndIsOn
User.current.available = !dndIsOn
FirestoreService.db.collection(Constants.Firestore.Collections.users).document(User.current.uid).updateData([Constants.Firestore.Keys.available : !dndIsOn])
if dndIsOn {
print("DND is on!")
setupDNDUI()
} else if !dndIsOn {
print("DND is off!")
setupActiveUI()
}
}
#IBAction func signOutTapped(_ sender: Any) {
let firAuth = Auth.auth()
do {
try firAuth.signOut()
} catch let signOutError as NSError {
print ("Error signing out: %#", signOutError)
}
print("Successfully signed out")
}
#IBAction func bumpTapped(_ sender: Any) {
self.performSegue(withIdentifier: Constants.Segues.toCall, sender: self)
}
#IBAction func friendsNavTapped(_ sender: Any) {
self.performSegue(withIdentifier: Constants.Segues.toFriends, sender: self)
}
#IBAction func profileNavTapped(_ sender: Any) {
let nav = self.navigationController //grab an instance of the current navigationController
DispatchQueue.main.async { //make sure all UI updates are on the main thread.
nav?.view.layer.add(CATransition().segueFromLeft(), forKey: nil)
nav?.pushViewController(ProfileVC(), animated: false)
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.setNavigationBarHidden(true, animated: true)
self.backgroundImageView.contentMode = UIView.ContentMode.scaleAspectFill
doNotDisturbLabel.isHidden = true
if !userName.isEmpty {
welcomeLabel.text = "Welcome Back, " + userName + "!"
} else {
welcomeLabel.text = ""
}
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return .darkContent
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let friendsVC = segue.destination as? FriendsVC else {
return
}
FirestoreService.db.collection(Constants.Firestore.Collections.users).document(User.current.uid).getDocument { (snapshot, err) in
if let err = err {
print(err.localizedDescription)
} else {
let data = snapshot!.data()!
let requests = data[Constants.Firestore.Keys.requests] as? [String]
if let requests = requests {
friendsVC.requests = requests
}
}
}
}
class FriendsVC: UIViewController {
//var friends: [Friend] = User.current.friends
var friends: [User] = []
var requests: [String]?
#IBOutlet weak var requestsNumberLabel: UILabel!
#IBOutlet weak var backgroundImageView: UIImageView!
#IBOutlet weak var friendRequestsButton: UIButton!
#IBOutlet weak var homeNavButton: UIButton!
#IBOutlet weak var friendsTitle: UILabel!
#IBOutlet weak var friendTableView: UITableView!
#IBOutlet weak var addFriendButton: UIButton!
#IBOutlet weak var tableViewTopConstraint: NSLayoutConstraint!
#IBAction func friendRequestsTapped(_ sender: Any) {
self.performSegue(withIdentifier: Constants.Segues.toRequests, sender: self)
}
#IBAction func homeNavTapped(_ sender: Any) {
let nav = self.navigationController //grab an instance of the current navigationController
DispatchQueue.main.async { //make sure all UI updates are on the main thread.
nav?.view.layer.add(CATransition().segueFromLeft(), forKey: nil)
nav?.pushViewController(HomeVC(), animated: false)
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.setNavigationBarHidden(true, animated: true)
backgroundImageView.contentMode = UIView.ContentMode.scaleAspectFill
friendTableView.backgroundView?.backgroundColor = .white
friendsTitle.isHidden = false
UserService.getUserArray(uids: User.current.friendUids, completion: { (users) in
guard let users = users else {
print("User has no friends")
return
}
self.friends = users
self.friendTableView.reloadData()
})
guard let requests = self.requests else {
friendRequestsButton.isHidden = true
requestsNumberLabel.isHidden = true
self.tableViewTopConstraint.constant = 0
return
}
requestsNumberLabel.text = requests.count.description
// Do any additional setup after loading the view.
friendTableView.delegate = self
friendTableView.dataSource = self
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let homeVC = segue.destination as? HomeVC {
homeVC.userName = User.current.firstName
} else if let requestsVC = segue.destination as? RequestsVC {
UserService.getUserArray(uids: self.requests!) { (requesters) in
if let requesters = requesters {
requestsVC.requesters = requesters
}
}
}
}
}
When my app loads into the home screen, there is no problem, and when a button is tapped to transition to FriendsVC, there is no problem. However, when I try to initiate the transition from HomeVC to ProfileVC or from FriendVC to HomeVC, I get the error: "Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value" at the self.backgroundImageView.contentMode = UIView.ContentMode.scaleAspectFill lines in my viewDidLoad methods. These segues have something in common in that these are the ones where I override the prepare() function, but I'm not sure what I'm doing wrong

Sigbart error during prepare for segue/unwind segue

I have an app where it is a marketplace and when you click on a product, it opens a detail view controller passing the data to display on the DetailVC. Additionally, inside the DetailVC, if you click on a button to claim the product, it segues to another VC to finalize the transaction.
In the DetailVC, there is a back button which is an unwind segue back to the main marketplace VC. Inside the TransactionVC, there is a cancel button which takes you back to the DetailVC.
When I am clicking the backButton in the DetailVC to take me back to the main market VC but I am getting a SIGBART Error and this :
020-07-15 09:05:23.707490-0500 evolutionatx[707:141952] Could not cast value of type 'evolutionatx.MarketplaceViewController' (0x1032c7868) to 'evolutionatx.PopUpPurchaseViewController' (0x1032c7ba8).
Here is the code for the DetailVC
import UIKit
import iCarousel
import CoreData
class MarketDetailViewController: UIViewController, UIScrollViewDelegate, iCarouselDelegate, iCarouselDataSource {
var productImageArray = [UIImage]()
var productVideo = String()
var pointsToPurchase = String()
var productName = String()
var productDescription = String()
var companyLogo = UIImage()
var companyWebsite = String()
var additionalProductImage = [UIImage]()
var companyName = String()
var promoCODE = String()
var buyLink = String()
var slides:[Slide] = [];
//IB
#IBOutlet weak var productNameLabel: UILabel!
#IBOutlet weak var productPriceLabel: UILabel!
#IBOutlet weak var productDescLabel: UILabel!
#IBOutlet weak var claimButton: UIButton!
#IBOutlet weak var imgScrollView: UIScrollView!
#IBOutlet weak var websiteButton: UIButton!
#IBOutlet weak var pageControl: UIPageControl!
#IBOutlet weak var logoDisplay: UIImageView!
#IBOutlet weak var carouselView: iCarousel!
#IBOutlet weak var otherProductslabe: UILabel!
var carouselImages = [UIImage]()
var evoCoin = Int()
override func awakeFromNib() {
super.awakeFromNib()
carouselImages = productImageArray
}
override func viewDidLoad() {
super.viewDidLoad()
valueSetter()
imgScrollView.delegate = self
slides = createSlides()
setupSlideScrollView(slides: slides)
pageControl.numberOfPages = slides.count
pageControl.currentPage = 0
view.bringSubviewToFront(pageControl)
}
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let pageIndex = round(scrollView.contentOffset.x/view.frame.width)
pageControl.currentPage = Int(pageIndex)
let maximumHorizontalOffset: CGFloat = scrollView.contentSize.width - scrollView.frame.width
let currentHorizontalOffset: CGFloat = scrollView.contentOffset.x
// vertical
let maximumVerticalOffset: CGFloat = scrollView.contentSize.height - scrollView.frame.height
let currentVerticalOffset: CGFloat = scrollView.contentOffset.y
let percentageHorizontalOffset: CGFloat = currentHorizontalOffset / maximumHorizontalOffset
let percentageVerticalOffset: CGFloat = currentVerticalOffset / maximumVerticalOffset
}
#IBAction func claimProduct(_ sender: Any) {
print("tap rec")
claimProductandPurchase()
}
func claimProductandPurchase(){
evoCOiner()
if(evoCoin >= Int(pointsToPurchase)!){
print("Transaction Successful")
performSegue(withIdentifier: "proceedQuestion", sender: self)
}
else{
showToast(controller: self, message: "Insufficient EvoCoins", seconds: 0.5)
}
}
func showToast(controller: UIViewController, message : String, seconds: Double) {
let alert = UIAlertController(title: nil, message: message, preferredStyle: .alert)
alert.view.backgroundColor = UIColor.black
alert.view.alpha = 0.6
alert.view.layer.cornerRadius = 15
controller.present(alert, animated: true)
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + seconds) {
alert.dismiss(animated: true)
}
}
func evoCoiner(){
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "EvoCoins")
request.returnsObjectsAsFaults = false
do{
let result = try context.fetch(request)
for data in result as! [NSManagedObject]
{
evoCoin = data.value(forKey: "evoCoins") as! Int
}
}catch{
print("Failed")
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let detailController = segue.destination as! PopUpPurchaseViewController
detailController.ppromo = promoCODE
detailController.link = buyLink
detailController.coinToPurchase = Int(pointsToPurchase)!
}
//This is the unwind used by the transaction back button
#IBAction func unwindToItem(segue: UIStoryboardSegue) {
}
}
Here is the code in the transaction VC
import UIKit
import AMTabView
import CoreData
class MarketplaceViewController: UIViewController, TabItem {
#IBOutlet weak var sView: UIView!
#IBOutlet weak var evoCoinLabe: UILabel!
//For the sake of simplicity I only kept the Unwind functions
//MARK: - UNWIND FUNCTIONS
#IBAction func unwindToMainMarketView(segue: UIStoryboardSegue) {
}
}
How can I fix this error?
Please advise if my question was not clear or properly phrased (if so, sorry I am pretty new to all of this)
As #matt already said in his comment and the error clearly states, you cannot cast a MarketplaceViewController to a PopUpPurchaseViewController.
Furthermore instead of doing a forced cast, always look for a safe one like below. Doing so will prevent crashes.
if let detailController = segue.destination as? PopUpPurchaseViewController {
...
}
else {
// log failed to cast
}

Swift: Why can I pass info from one IBAction to another, but not to a function

I am trying to get the values for splitValue and tipPercent into the getSettings() at the bottom. Why can I get the values for both of those in the IBAction calculatePressed, but when I try to get the values into the function the value is nil. I am sooo confused. Thank you for the help!
#IBOutlet weak var billTextField: UITextField!
#IBOutlet weak var zeroPctButton: UIButton!
#IBOutlet weak var tenPctButton: UIButton!
#IBOutlet weak var twentyPctButton: UIButton!
#IBOutlet weak var splitNumberLabel: UILabel!
var tipChosen = ""
var totalPerPerson = ""
var tipPercent = ""
var splitValue = ""
#IBAction func tipChanged(_ sender: UIButton) {
tipPercent = sender.currentTitle!
if sender.isSelected == true {
return
}
zeroPctButton.isSelected = false
tenPctButton.isSelected = false
twentyPctButton.isSelected = false
sender.isSelected = true
if sender.currentTitle == "0%" {
tipChosen = "0.00"
} else if sender.currentTitle == "10%" {
tipChosen = "0.10"
} else if sender.currentTitle == "20%" {
tipChosen = "0.20"
}
billTextField.endEditing(true)
}
#IBAction func stepperValueChanged(_ sender: UIStepper) {
splitValue = String(Int(sender.value))
splitNumberLabel.text = String(Int(sender.value))
}
#IBAction func calculatePressed(_ sender: UIButton) {
let bill = Float(billTextField.text!)!
let tip = Float(tipChosen)!
let tax = bill * tip
let splitNumber = Float(splitNumberLabel.text!)
let total = (bill + tax) / Float(splitNumber!)
totalPerPerson = "$\(String(format: "%.2f", total))"
performSegue(withIdentifier: "goToTotal", sender: self)
}
func getSettings() -> String {
return "Split between \(splitValue) people, with a \(tipPercent) tip."
}
Ok, sorry it took me a bit, but I finally think I understand what I did.
class CalculatorViewController: UIViewController {
var tip = 0.0
var finalBill = ""
var split = 2
#IBOutlet weak var billTextField: UITextField!
#IBOutlet weak var zeroPctButton: UIButton!
#IBOutlet weak var tenPctButton: UIButton!
#IBOutlet weak var twentyPctButton: UIButton!
#IBOutlet weak var splitNumberLabel: UILabel!
#IBAction func tipChanged(_ sender: UIButton) {
if sender.isSelected == false {
sender.isSelected = false
} else if sender.isSelected == true {
sender.isSelected = true
}
zeroPctButton.isSelected = false
tenPctButton.isSelected = false
twentyPctButton.isSelected = false
sender.isSelected = true
billTextField.endEditing(true)
}
#IBAction func stepperValueChanged(_ sender: UIStepper) {
splitNumberLabel.text = Int(sender.value).description
}
#IBAction func calculatePressed(_ sender: UIButton) {
if zeroPctButton.isSelected == true {
tip = 0.0
} else if tenPctButton.isSelected == true {
tip = 0.1
} else if twentyPctButton.isSelected == true {
tip = 0.2
}
print(tip)
let bill = Double(billTextField.text!)
split = Int(Double(splitNumberLabel.text!)!)
if billTextField.text != "" {
let billWithTip = (bill! * tip) + bill!
let billWithTipSplit = billWithTip / Double(split)
finalBill = String(format: "%.2f", billWithTipSplit)
print(billWithTip)
}
self.performSegue(withIdentifier: "getResults", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "getResults" {
let destinationVC = segue.destination as! ResultsViewController
destinationVC.finalBill = finalBill
destinationVC.split = split
destinationVC.tip = tip
}
}
}
class ResultsViewController: UIViewController {
#IBOutlet weak var totalLabel: UILabel!
#IBOutlet weak var settingsLabel: UILabel!
var tip = 0.0
var split = 2
var finalBill = ""
override func viewDidLoad() {
super.viewDidLoad()
totalLabel.text = "$\(finalBill)"
settingsLabel.text = "Split between \(Int(split)) people, with a \(Int(tip * 100))% tip"
}
#IBAction func recalculatePressed(_ sender: UIButton) {
dismiss(animated: true, completion: nil)
}
}
I did what you suggested with the string and some minor calculations on the second view controller, changed the values of a few declared properties and got rid of the getSettings(). I was under the impression that I couldn't pass data without a return value from a function. Thank you for the help!

SWIFT - Get value from JSON and pass through Navigation Controller

As a title I need to pass data from a JSON to my Home class, but between the two classes there is a navigation controller
Then I would like to know how to pass data through it. Thanks to everyone for the advice.
Storyboard. Login Class:
let title = list["title"] as! String
let description = list["tagline"] as! String
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let navVC = segue.destination as! UINavigationController
let tableVC = navVC.viewControllers.first as! Home
tableVC.title = "Test"
tableVC.description = "Test"
}
Home Controller:
#IBOutlet weak var titleActivity: UILabel!
#IBOutlet weak var descriptionActivity: UILabel!
var description = ""
var title = ""
override func viewDidLoad() {
super.viewDidLoad()
titleActivity.text = nome //NIL
descriptionActivity.text = descrizione //NIL
}

Transfer TextField data between ViewControllers

I have ViewController, which has a textfield and a button. The user enters his name into the textfield and hits the DONE button. When he hits the button, he is segued to GifteeDetails, which is a different view controller. There is a label in that viewController that is supposed to display his name. But, his name doesn't show up. I don't receive an error.
Here's ViewController:
#IBOutlet weak var textGifteeName: UITextField!
#IBAction func toDetails(_ sender: Any) {
performSegue(withIdentifier: "toDetails", sender: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let destDetails: GifteeDetails = segue.destination as! GifteeDetails
destDetails.nametext = textGifteeName.text!
destDetails.agetext = "\(Int(age)! - 2000 + 17)"
destDetails.locationstext = labelGifteeLocationsPreview.text!
destDetails.intereststext = labelGifteeInterestsPreview.text!
}
Here's GifteeDetails:
var nametext = String()
var agetext = String()
var locationstext = String()
var intereststext = String()
#IBOutlet weak var labelGifteeName: UILabel!
#IBOutlet weak var labelGifteeAge: UILabel!
#IBOutlet weak var labelGifteeLocations: UILabel!
#IBOutlet weak var labelGifteeInterests: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
nametext = labelGifteeName.text!
agetext = labelGifteeAge.text!
locationstext = labelGifteeLocations.text!
intereststext = labelGifteeInterests.text!
}
Sorry about all the !. Swift gives me an error unless I have them.
You are updating the strings nametext and others, which are not connected to your labels.
You need to replace this piece of code:
destDetails.nametext = textGifteeName.text!
destDetails.agetext = "\(Int(age)! - 2000 + 17)"
destDetails.locationstext = labelGifteeLocationsPreview.text!
destDetails.intereststext = labelGifteeInterestsPreview.text!
With:
destDetails.labelGifteeName.text = textGifteeName.text!
destDetails.labelGifteeAge.text = "\(Int(age)! - 2000 + 17)"
destDetails.labelGifteeLocations.text = labelGifteeLocationsPreview.text!
destDetails. labelGifteeInterests.text = labelGifteeInterestsPreview.text!
nametext is a String object, and it is different from labelGifteeName.text which is the String of the label you want to update.
If you use segue then pls remove it, and then create action method of that button in the FirstviewController and use pushViewController to move on the SecondviewController.
Swift 3
Example:
#IBAction func toDetails(_ sender: Any)
{
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "SecondView") as! SecondView
UserDefaults.standard.set(self. textGifteeName.text, forKey: "savedStringKey")
UserDefaults.standard.synchronize()
self.navigationController?.pushViewController(vc,
animated: true)
}
In SecondView:
override func viewDidLoad() {
super.viewDidLoad()
self.labelGifteeName.text = UserDefaults.standard.string(forKey: "savedStringKey")!
}
Please Update your func viewDidLoad() method in GifteeDetailsVC with labelGifteeName.text! = nametext , labelGifteeAge.text!= agetext instead of your code because you have already assigned the value in to strings i.e, nametext and agetext in ViewController, you need to display string value in label