Initializing class issue using multiple ViewControllers (Swift) - ios

I'm working with two view controllers in my XCode project. I passed a variable from the first View Controller to the second. And I keep getting this Swift Complier Error: "Class SecondViewController has no initializers" Fix-it > Stored property 'XScore' without initial value prevents synthesised initializers.
This is how I passed the variable in the FirstViewController:
override func prepareForSegue(Segue: UIStoryboardSegue, sender: AnyObject?) {
var Destination = (Segue.destinationViewController as SecondViewController)
Destination.XScore = Score
}
This is (part of) my Second View Controller:
class SecondViewController: UIViewController {
#IBOutlet weak var XScoreLabel: UILabel!
var XScore:Double = 0.00
I have tried many different ways to initialize this property even when it seems right to me without.
Thanks in advance.

You have a couple of problems. As Gabe points out, you are casting your destination as a type SecondViewController, but are showing code for a different class, ShopViewController.
You need to show us the declaration of XScore in SecondViewController.
If it's defined (in SecondViewController) as
var XScore:Double
then change it to
var XScore:Double = 0
or
var XScore:Double? = nil
(to allow for nil values)

Related

How can I save data (score) from my first viewcontroller to the second?

I am creating a quiz app for ios for a big project at my school, but it is my first time making something with xcode and swift. I've run into a problem which i can't seem to figure out on my own. At the moment I am trying to keep score across multiple ViewControllers; I've tried a lot of different stuff, but it still doesn't work.
I am, like I said, a noob to xcode and actually all coding.
At first i have the code for the score:
#IBOutlet weak var scoreLabel: UILabel!
var score = 0
In my app, when someone answers the right question, the score gets 1 point (score += 1). After 4 questions of the first theme, 4 questions of a different theme, on a different ViewController are showed.
this is my code at the end of the first ViewController (MultipleChoice 2 is my second vc):
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.destination is MultipleChoice2 {
let vc = segue.destination as? MultipleChoice2
vc?.score = score
}
}
Code for my second vc:
var score = Int()
#IBOutlet weak var scoreLabel: UILabel?
override func viewDidLoad() {
super.viewDidLoad()
scoreLabel?.text = String(score)
}
I might be doing this all wrong, but I expect the app to save the score from my first ViewController to my second, so that, when someone has score 3 points in the first vc, he still has 3 points in the second vc.
The other things that i have tried didn't work at all, but with this code i get the message Thread 1: signal SIGABRT, at class AppDelegate, after clicking on the button that sends me to the second viewcontroller.
I honestly have no clue on how to save my score to the next vc and it would be great if someone had a solution to this..
Check if all the outlets are connected. After that check whether segue is connected or not.
Change
var score = Int()
to
var score: Int = 0
in secondVC and try again.
Try following and let me know if you face any trouble getting it working :
First of all change declaration of score variable in secondVC to
var score : Int = 0
then in prepare for segue method you can try these :
first set segue identifier from storyboard as it is more convenient to use :
then you can check if we are in proper condition by
if segue.identifier == "showSecondVC" {
let vc = segue.destination as! MultipleChoice2
vc.score = score
}
Also use breakpoint to check whether everything is executing properly
One more thing is you can set label text by using following code also :
scoreLabel?.text = "\(score)"
Change var score = Int() to var score: Int? in second view controller.
Set segue identifier to second view controller in storyboard.
Modify segue method as below in first view controller
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showSecondVC" {
let vc = segue.destination as! MultipleChoice2
vc.score? = score
}
}

Viewcontroller loaded twice

I am trying create Tab Bar application. I added to the first viewcontroller a button and a text field. When I wrote some text and pushed button I expected that loads second viewcontroller and in the label field appears text from the text field.
I have 2 problems.
Second viewcontroller loaded twice.
when I push button on tab bar and load first viewcontroller again? me text disappear.
I only start work with Xcode. Help me please and describe resolve in details🙏
import UIKit
var tfTextString: String = ""
class FirstViewController: UIViewController {
#IBOutlet weak var textField: UITextField!
#IBAction func enter(_ sender: Any) {
if textField.text != "" {
//performSegue(withIdentifier: "segue", sender: self)
//tfTextString = textField.text!
self.tabBarController?.selectedIndex = 0
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
var secondController = segue.destination as! SecondViewController
secondController.myString = textField.text!
}
}
import UIKit
class SecondViewController: UIViewController {
#IBOutlet weak var label: UILabel!
var myString = String()
override func viewDidLoad() {
super.viewDidLoad()
label.text = myString
}
}
It looks like your view controller is loaded twice because you are loading it twice.
Don't think of each screen on the storyboard as being it's own distinct entity as per the singleton pattern. They aren't. They're their own distinct class / view combination, which gets instantiated (loaded) once every time you call it. To put it another way, you aren't looking at the 'actual' classes in the storyboard, you're looking at the template that's used to build the class when called for. So you're calling for it twice, in two different places, and it happily produces multiple copies of the underlying view / controller for you.
I think you don't want to use a navigation controller to 'push' the view back onto the stack. You're going to want to tell the tab bar to switch to a different view instead.
The swift version of the code you're looking for is:
self.tabBarController?.selectedIndex = 1
I think the objective-C equivalent would have been...
[[self tabBarController] setSelectedIndex:1];
But it's been long enough since I've done swift I may have overlooked something important in there.

Items in function become nil when function is called - Swift 3

I have a custom class segmented control that is communicating with a view controller every time a different item in the segmented control is selected. I'm able to pass data to the view controller just fine.
#IBDesignable class SegmentedControlLeft: UIControl {
var selectedIndex: Int = 0 {
didSet {
displayNewSelectedIndex()
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let messagesViewController: MessagesViewController = storyboard.instantiateViewController(withIdentifier: "PurpleVC") as! MessagesViewController
messagesViewController.animateViews(selectedIndex: selectedIndex)
}
}
...some more code
}
However, when I try to use some basic logic every time the passed variable is updated, all the items inside the "animateViews" function apparently turn nil. I receive the infamous "unexpectedly found nil" error. This only happens when i try to use the variable I'm passing, everything runs as expected otherwise outside of the function.
import UIKit
class MessagesViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
#IBOutlet weak var messageTableView: UITableView!
#IBOutlet weak var boostCollectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
...all cells, delegates, and datasource are registered and setup in here.
}
func animateViews(selectedIndex: Int) {
if selectedIndex == 0 {
print("LEFT") // prints as expected
self.messagesTableView.isHidden = false // unexpected found nil error on each of these items.
self.boostCollectionView.isHidden = true
} else if selectedIndex == 1 {
print("RIGHT") // prints correctly when updated
self.messageTableView.isHidden = true
self.boostCollectionView.isHidden = false
}
}
}
I've been playing around with this for far too long, how do I get views in the animateViews function to hide and unhide using the variable I'm passing from the segmented Control?
There is probably an amazingly simple fix that I'm not getting here, but I appreciate you for getting your eyeballs this far. Thank you!
This is happening because by the time you call
messagesViewController.animateViews(selectedIndex: selectedIndex), the MessagesViewController is not instantiated. That also means that the Table view and Collection View are not drawn yet and you are trying to use it before it is being created.
The viewDidLoad() method has to be called once to use the UI Elements of the View Controller.
Each time this line is called let messagesViewController: MessagesViewController = storyboard.instantiateViewControl, a new instance of the view controller is created.
Suggestion:
My suggestion will be to create an outlet of the segmented controls(seems like you are having multiple Segmented Controls in View Controller), and create a method like #IBAction func indexChanged(_ sender: AnyObject) { and assign this action to the value changed property of the segmented controls.
You can differentiate among the segmented controls using the sender parameter.

Transport Data from View Controllers in Swift 2

So I started working on a basic app and ran into an issue. The user is taken to a first view controller where they are asked to enter two team names and then press the start button (which performs a segue to the second view controller). On the second view controller are two labels (named homeIdentifier and awayIdentifier ) and I want the labels on the second view controller to update to the names the user put in on the first. I thought it would be simple, but have run into an issue; it says "fatal error: unexpectedly found nil while unwrapping an Optional value
(lldb)" It also says in red "Thread 1 EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP subcode=0x0)"
How do I do this? The start button is set to perform a segue to the second view controller already, so I don't know if something is going wrong with that process. I've tried the suggestions I've seen here but doesn't seem to work (I don't know if it's because I'm using swift 2, or because I'm transporting 2 variables, etc).
I don't think transporting two variables is the problem. Just make sure that you only perform the segue of there is two variables and one of them does not equal nil. Make sure to also create two variables in the second view controller as you will need to transfer the data over from your first view controller into those variables in the second view controller. To pass data in swift 2 I would use the prepare for segue method. You will also have to set and ID in the attributed inspector for the segue that takes you from the first view controller to the second view controller.
this goes in the first view controller .swift file
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if (segue.identifier == "segueIdentifier") {
// make an instance of the second view controller
var detailVC = segue!.destinationViewController as DetailViewController;
detailVC.variable1 = "\(firstViewControllerVariable)"
detailVC.variable2 = "\(secondViewControllerVariable)"
}
}
this is what your second view controller .swift file should consist of
also make sure all your outlets are linked correctly
import UIKit
class secondViewController: UIViewController {
var variable1: String = String()
var variable2: String = String()
override func viewDidLoad() {
super.viewDidLoad()
homeIdentifier.text = variable1
awayIdentifier.text = variable2
}
}
hope this helps!
FirstVC
let sc=self.storyboard?.instantiateViewControllerWithIdentifier("SecondVC")as! SecondVC
sc.str1="Pass Text"
self.navigationController?.pushViewController(sc, animated:true)
SecondVC
class ViewDetails: UIViewController
{
var str1:String!
override func viewDidLoad()
{
super.viewDidLoad()
print(str1)
// Do any additional setup after loading the view.
}
}

(Swift) PrepareForSegue: fatal error: unexpectedly found nil while unwrapping an Optional value

DetailViewController:
#IBOutlet var selectedBundesland: UILabel!
TableViewController:
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if (segue.identifier == "BackToCalculator") {
var vc:FirstViewController = segue.destinationViewController as FirstViewController
vc.selectedBundesland.text = "Test"
}
IBOutlet is connected!
Error: fatal error: unexpectedly found nil while unwrapping an Optional value
I read multiple pages about Optionals but i didn't know the answer to my problem.
Do you need more information about my project?
You cannot write directly to the UILabel in prepareForSegue because the view controller is not fully initialised yet. You need to create another string property to hold the value and put it into the label in the appropriate function - such as viewWillAppear.
DetailViewController:
var textValue: String = ""
#IBOutlet weak var selectedBundesland: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
selectedBundesland.text = textValue
}
TableViewController:
if (segue.identifier == "BackToCalculator") {
var vc:FirstViewController = segue.destinationViewController as FirstViewController
vc.textValue = "Test"
}
Recently had this problem. The problem was that I had dragged the segue from a specific object from my current view controller to the destination view controller - do not do this if you want to pass values.
Instead drag it from the yellow block at the top of the window to the destination view controller. Then name the segue appropriately.
Then use the if (segue.identifier == "BackToCalculator") to assign the value as you are currently. All should work out!
I just had the same problem, I solved it by defining a string that is not connected to an outlet in the new view controller and than referring to it in the prepareForSegue() method, in the new VC I made the label outlet to take the value of the non connected string in the viewDidLoad() method.
Cheers
While the correct solution is to store the text and attach it to the label later in viewDidLoad or something, for testing proposes, you can bypass the issue by forcing the destinationViewController to build itself from storyboard by calling its view property like:
override func prepare(for segue: UIStoryboardSegue, sender: Any?){
if (segue.identifier == "TestViewController") {
var vc:FirstViewController = segue.destination as! TestViewController
print(vc.view)
vc.testLabel.text = "Hello World!"
}
}
made for Swift 3.0 with love

Resources