I'm trying to get a quiz function to work, and my problem is that I'm trying to print the score on a second view controller. When i run the simulator it takes the user to the second view controller yet the score does not print.
import UIKit
var CurrentQuestion = "" // global variable to be accessed
class ViewController: UIViewController {
// create an array to store queations
let questions = ["favourite pet?", "favourite colour", "where was i born"]
// multiple arrays that contain strings, right anwser is first
let anwsers = [["dog", "cat", "bird"], ["blue", "black", "green"], ["tokyo", "tennese", "england"]]
// variables
// keeps track of what question were at
var currentQuestion = 0
var rightAnwserPlacement:UInt32 = 0 // where right anwser is located
var points = 0; // counts number of points - score
//lbl
#IBOutlet weak var lbl: UILabel!
//button
#IBAction func btn(_ sender: UIButton) {
if (sender.tag == Int(rightAnwserPlacement))
{
print ("right")
points += 1
}
else
{
print ("wrong")
}
if (currentQuestion != questions.count)
{
newQuestion()
}
else
{
performSegue(withIdentifier: "showScore", sender: self)
}
}
override func viewDidAppear(_ animated: Bool) {
newQuestion()
}
// function that displays a new question sets up interface for the new questions
func newQuestion()
{
lbl.text = questions[currentQuestion]
rightAnwserPlacement = arc4random_uniform(3)+1 // random button
// create a button
var button:UIButton = UIButton()
var x = 1
for i in 1...3
{
// create a button
button = view.viewWithTag(i) as! UIButton
if (i == Int(rightAnwserPlacement)) // checks to see if it matches the right anwser
{
button.setTitle(anwsers[currentQuestion][0], for: .normal)
}
else {
button.setTitle(anwsers[currentQuestion][x], for: .normal)
x = 2
}
}
currentQuestion += 1
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
The second view controller is
import UIKit
class second: UIViewController {
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
label.text = CurrentQuestion
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
You have set CurrentQuestion as a global string variable and later on try to increment its value by 1? What is that supposed to mean? Additionally, you have a local variable in the class with the same name as currentQuestion. Granted the two are different variables, but you should seriously consider renaming your variables.
Also, you are changing value of this local variable and no-where changing the value of the global variable. Most likely this mix-up is because of similar naming of variables.
And just a suggestion, avoid using global variables to pass data. Look into delegate-protocol way of passing data between VCs.
Related
I am creating a simple counter application where the user can set a maximum number of patrons allowed in their place of business and when that number is reached they're given a notification that they're at maximum capacity. I'm using UserDefaults to store this data, however whenever I update the max number in the settings and return to the main view it's still using the previous max number. If I close the app and reopen it has the correct data. How do I make sure the data is being updated in the main view controller so it receives proper data?
Code
ViewController.swift (main view controller)
import UIKit
import AVFoundation
class ViewController: UIViewController {
var counter = 0;
var max = UserDefaults.standard.integer(forKey: "max");
// Max patron alert function
func showAlert() {
let alertController = UIAlertController(title: "Limit Reached", message: "Your place of business has reached maximum capacity, please stop patrons from entering until capacity dissipates.", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Dismiss", style: .default))
self.present(alertController, animated: true, completion: nil)
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate)
}
#IBOutlet weak var count: UILabel!
#IBAction func increment(_ sender: UIButton) {
if counter < max {
counter += 1;
count?.text = String(counter);
}
if counter == max {
showAlert();
}
}
#IBAction func decrement(_ sender: UIButton) {
if (counter == 0) {
counter += 0;
}
else {
counter -= 1;
}
count?.text = String(counter);
}
#IBAction func reset(_ sender: UIButton) {
counter = 0;
count?.text = String(counter);
}
override func viewDidLoad() {
super.viewDidLoad()
// UserDefaults.standard.synchronize()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
SettingsViewController.swift
import UIKit
class SettingsViewController: UIViewController {
var mainViewController:ViewController?;
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBOutlet weak var maxOccupancy: UITextField!
#IBAction func save(_ sender: UIButton) {
let max = Int(maxOccupancy.text!);
UserDefaults.standard.set(max, forKey: "max")
UserDefaults.standard.synchronize()
print(UserDefaults.standard.integer(forKey: "max"))
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
*/
}
This line:
var max = UserDefaults.standard.integer(forKey: "max")
is executed only once, when ViewController is created. (BTW: no need for semicolons in Swift). Whatever happens to UserDefaults afterwards won't be reflected in its value.
Replace the line with
var max: Int {
UserDefaults.standard.integer(forKey: "max")
}
so that every access to it actually goes out to fetch the value from UserDefaults.
These constructs are called "Computed Properties", explained here.
This happens because you read the counter number only once when ViewController is allocated. You might try to re-read it when ViewController will appear on the screen:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
max = UserDefaults.standard.integer(forKey: "max")
}
Also, no need to use ; in every line's ending, Swift doesn't require it.
I am looking to pass data through a text box, and into an array (Second View Controller), so it can be added to a pool of data and randomly chosen.
For instance, you would input your name (and/or another name) into the text field in the First VC and and it would go into an array that would tell them which color they are shown in the Second VC.
Essentially, I'm trying to pass the data to a variable (which will hold all the names inputed), and append the variable to the array, then click a button to generate and randomly pull from the array via arc4random. My problem is its not appending. Any help would be greatly appreciated.
Here is my code:
VC 1:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func StartTapped(_ sender: Any) {
performSegue(withIdentifier: "SecondViewControllerSegue", sender: self)
}
#IBOutlet weak var AddPlayerTextField: UITextField!
#IBAction func AddTexttoArrayBtn(_ sender: Any) {
if AddPlayerTextField.text == nil
{
performSegue(withIdentifier: "SecondViewControllerSegue", sender: self)
}
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let SecondViewController = segue.destination as! ViewController2
SecondViewController.myString = AddPlayerTextField.text!
VC 2:
import UIKit
class ViewController2: UIViewController {
var myString = String ()
var TeamBuildingQuestions = [ "is Red", "is Blue", "is Green", "is Purple","is Teal","is Orange", "is Red", "is Grey", "is Pink"]
var MasterTeamBuildingQuestionsArray = [ "Hello", "beep bop bbeep", "Boop", "Bap","Bospop","bob the builder"]
#IBOutlet weak var DisplayArray: UILabel!
#IBAction func NextQuestionBtn(_ sender: Any) {
let RandomArrayNumber = Int (arc4random_uniform(UInt32(TeamBuildingQuestions.count)))
let QuestionDisplayed = TeamBuildingQuestions[RandomArrayNumber]
DisplayArray?.text = QuestionDisplayed
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
TeamBuildingQuestions.append(myString)
}
In VC2, Instead of didReceiveMemoryWarning, implement TeamBuildingQuestions.append(myString) in viewDidLoad.
Here is a bug:
if AddPlayerTextField.text == nil{
}
should be
if AddPlayerTextField.text != nil && AddPlayerTextField.text.count > 0{
}
there was a question like this already made but I tried the suggestions and it didn't work for me still. :/
I am trying to make a quiz app using this tutorial: https://www.youtube.com/watch?v=KNAQ3Y8PGkM&t=1s
Here is a link to download my project: https://www.dropbox.com/s/bwvg6fyrrzudued/kat%20quiz.zip?dl=0
Everything is perfect except I get the error:
Could not cast value of type 'UIView' (0x10d54a4c0) to 'UIButton' (0x10d552120)
I'd really appreciate some help. I tried everything in my knowledge and searching, thank you. :)
import UIKit
class ViewController: UIViewController {
let questions = ["What color is Courage the Cowardly Dog?", "What is the name of the woman that lives with Courage?", "What is the name of the man that lives with Courage?"]
let answers = [["Purple","Grey","Orange"],["Muriel", "Angela", "Elizabeth"], ["Eustace", "Robert", "Ezio"]]
//Variables
var currentQuestion = 0
var rightAnswerPlacement:UInt32 = 0
var points = 0
//Label
#IBOutlet weak var lbl: UILabel!
//Button
#IBAction func action(_ sender: AnyObject) {
if (sender.tag == Int(rightAnswerPlacement))
{
print ("RIGHT!")
points += 1
}
else
{
print ("WRONG!!!!!!")
}
if (currentQuestion != questions.count)
{
newQuestion()
}
else{
performSegue(withIdentifier: "showScore", sender: self)
}
}
override func viewDidAppear(_ animated: Bool) {
newQuestion()
}
//Function that displays new question
func newQuestion(){
lbl.text = questions[currentQuestion]
rightAnswerPlacement = arc4random_uniform(3)+1
//Create a button
var button:UIButton = UIButton()
var x = 1
for i in 1...3
{
//Create a button
button = view.viewWithTag(i) as! UIButton
if (i == Int(rightAnswerPlacement))
{
button.setTitle(answers[currentQuestion][0], for: .normal)
}
else
{
button.setTitle(answers[currentQuestion][x], for: .normal)
x = 2
}
}
currentQuestion += 1
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Did you create UI Button in your Story Board ??
In your youtube video, watch 1:45
And remember to set your 3 UI Button's tag to 1, 2 and 3
watch 3:20
Your problem occurs because you've set your View's tag = 1.
You can fix it easily by change View's tag value to a number != 1, 2 and 3
Because your ViewController have 2 view with the same tag = 1. (view and UI Button).
So when you use view.viewWithTag(i), It accidentally select view not UI Button. So it can not convert to UI Button type
P/s: next time, if you want to select your Button directly, you can make an #IBOutlet for that Button as you make with your Label. It will not cause confusing error like that
I am trying to pass data between my two view controllers in my UIscrollview. I am trying to use delegation to send data between Viewcontroller1 and Viewcontroller2. The delegate is Viewcontroller, while the delegator is Viewcontroller1 and Viewcontroller2.
In the code posted below, when the switch in Viewcontroller1 is toggled, it makes the switch in Viewcontroller2 put to the "off" state. I keep on getting the
fatal error: unexpectedly found nil while unwrapping an Optional value
error when I run it, but I have no clue what is causing this problem. Any ideas why?
Below is the Viewcontroller that contains the Uiscrollview and the subviews/childviews
import UIKit
class ViewController: UIViewController, testing {
var vc1 = ViewController1(nibName: "ViewController1", bundle: nil)
var vc2 = ViewController2(nibName: "ViewController2", bundle: nil)
#IBOutlet weak var scrollView: UIScrollView!
func test1() {
vc2.switch2.on = false
}
override func viewDidLoad() {
super.viewDidLoad()
self.addChildViewController(vc1)
self.scrollView.addSubview(vc1.view)
vc1.didMoveToParentViewController(self)
var frame1 = vc2.view.frame
frame1.origin.x = self.view.frame.size.width
vc2.view.frame = frame1
self.addChildViewController(vc2)
self.scrollView.addSubview(vc2.view)
vc2.didMoveToParentViewController(self)
self.scrollView.contentSize = CGSizeMake(self.view.frame.size.width * 2, self.view.frame.size.height);
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
here is the Viewcontoller1 code
protocol testing{
func test1()
}
class ViewController1: UIViewController {
var delegate:testing?
#IBOutlet weak var switch1: UISwitch!
override func viewDidLoad() {
super.viewDidLoad()
let vc = ViewController()
self.delegate = vc
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func switch1toggled(sender: AnyObject) {
delegate?.test1()
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
and here is the Viewcontroller 2 code
import UIKit
class ViewController2: UIViewController {
#IBOutlet weak var switch2: UISwitch!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func switch2toggled(sender: AnyObject) {
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
sorry for the long post, I have been stuck for a week on how to change the state of another switch from toggling a switch in another class, and this was the most efficient way that I found
Try This:
ViewController1
class ViewController1: UIViewController {
let defaults = NSUserDefaults.standardUserDefaults()
let switch1Key = "view1Switch"
override func viewWillAppear(animated: Bool) {
view1Switch.on = defaults.boolForKey(switch1Key)
}
#IBOutlet weak var view1Switch: UISwitch!
#IBAction func view1SwitchChanged(sender: UISwitch) {
defaults.setBool(view1Switch.on, forKey: switch1Key)
}
}
ViewController2
class ViewController2: UIViewController {
let defaults = NSUserDefaults.standardUserDefaults()
let switch1Key = "view1Switch"
override func viewWillAppear(animated: Bool) {
view2Switch.on = defaults.boolForKey(switch1Key)
}
#IBOutlet weak var view2Switch: UISwitch!
#IBAction func view2SwitchChanged(sender: UISwitch) {
defaults.setBool(view2Switch.on, forKey: switch1Key)
}
}
This method syncs the state of the two UISwitches using viewWillAppear and NSUserdefaults. The basic thought pattern is that you save the state of the switch to NSUserdefaults so that when either ViewController1 or ViewController2 is instantiated the view1Switch or view2Switch outlet's .on property is set to the saved value.
Caveats:
The first value for the switch when ViewController1 is instantiated (in the first app run) will be off because boolForKey returns false when there is no saved value. This can be hacked by using view1Switch.on = true directly after view1Switch.on = defaults.boolForKey(switch1Key)
This method makes the switches have the same value. In order to make them have different values, you can use a ! operator like so in ViewController2 view2Switch.on = !defaults.boolForKey(switch1Key). This way switch 1 will always be the opposite of switch 2.
I recommend this method over delegation because, while delegation is powerful, its power doesn't seem needed here.
If you have any questions please ask! :D
I have a UITextField named 'appleQuantity', when I try to convert it to an int to do math with it, it gives me an error named 'ViewControllerApple.Apple.Type does not have a member named appleQuantity'
Here's the code, the error is in 'var b:Int=appleQuantity.text.toInt()'
import UIKit
class ViewControllerApple: UIViewController {
//80, 0, 0, 0, 22, 0, 2, 20, 2, 2
class Apple {
#IBOutlet weak var appleQuantity: UITextField!
#IBAction func addApple(sender: UIButton) {
}
var b:Int=appleQuantity.text.toInt()
// var appleCal = 80 * b
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
I'm Really new to swift, so the more details provided, the more helpful it is!
Thanks so much in advance!
UPDATED Answer:
I feel that you're thinking of things the wrong way slightly. Here is how I propose you try and achieve what you're doing.
import UIKit
class ViewControllerApple: UIViewController {
class Apple {
var b: Int?
init() {
}
func getAppleCalculation() -> Int {
if let unwrappedB = b {
return unwrappedB * 80
}
else {
return 0
}
}
}
#IBOutlet weak var appleQuantity: UITextField!
#IBAction func addApple(sender: UIButton) {
}
override func viewDidLoad() {
super.viewDidLoad()
// This is where the view has loaded. Your buttons and textviews will
// have loaded and you can perform any actions on them.
var apple = Apple()
if let unwrappedAppleQuantityInt = appleQuantity.text.toInt() {
apple.b = unwrappedAppleQuantityInt
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
OLD ANSWER: Your variable b will actually never contain anything except the default value.
You should assign your Int b in your class' viewDidLoad, or following your structure, in your object's init function:
class Apple {
#IBOutlet weak var appleQuantity: UITextField!
#IBAction func addApple(sender: UIButton) {
}
var b: Int
init() {
if let unwrappedAppleQuantityInt = appleQuantity.text.toInt() {
b = unwrappedAppleQuantityInt
}
else {
// assign b a default value
b = 0
}
}
}
or you could do return a computed value, provided you only care about the output rather than storing the value, like so:
var b:Int {
get {
if let unwrappedAppleQuantityInt = appleQuantity.text.toInt() {
return unwrappedAppleQuantityInt
}
else {
return 0
}
}
}