QUIZ app ... UIAlertController not coming up and not restarting back to initial question - swift

The nextQuestion() and the startOver() function I believe is where it is giving me trouble... when I go through the questions it get to the end and does not pop up the alert on the screen just have to hit the True or false one and then it will startOver() but then it skips the first one and goes directly to the second question
import UIKit
class ViewController: UIViewController {
//Place your instance variables here
let allQuestions = QuestionBank()
var pickedAnswer : Bool = false
var questionNumber : Int = 0
#IBOutlet weak var questionLabel: UILabel!
#IBOutlet weak var scoreLabel: UILabel!
#IBOutlet var progressBar: UIView!
#IBOutlet weak var progressLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let firstQuestion = allQuestions.list[0]
questionLabel.text = firstQuestion.questionText
//when app loads it will display the first question in the array of question bank
}
#IBAction func answerPressed(_ sender: AnyObject) {
if sender.tag == 1 {
pickedAnswer = true
}
else if sender.tag == 2 {
pickedAnswer = false
}
//above - will change the picked answer based on which tag # is pressed
checkAnswer()
questionNumber = questionNumber + 1
nextQuestion()
}
func updateUI() {
}
func nextQuestion() {
if questionNumber <= 12 {
questionLabel.text = allQuestions.list[questionNumber].questionText
//if the current questionNumber is equal to or less than 12 then it will change the display text to the current question
}
else {
let alert = UIAlertController(title: "Congrats!", message: "Do you want to restart?", preferredStyle: .alert)
let restartAction = UIAlertAction(title: "Restart", style: .default, handler: { UIAlertAction in
self.startOver()
})
alert.addAction(restartAction)
present(alert, animated: true, completion: nil)
}
}
func checkAnswer() {
let correctAnswer = allQuestions.list[questionNumber].answer
if correctAnswer == pickedAnswer {
print("You got it!")
}
else {
print("Wrong!")
}
}
func startOver() {
questionNumber = 0
nextQuestion()
}
}

its a firmware issues. just updated to the latest IOS 13 update and the simulator ran the app no issues so I believe it is with my firmware being the latest versus Xcode with the previous one. it ran no issues on the simulator. simulator on 13.1.1 and I just uploaded to 13.1.3 that's the only difference. I had looked at the completed app from the course and its all line for line the same except the firmware.app running on Xcode simulator

Related

Trying to call a function in my code, not working

So I have created a function called startover1() and I am trying to call it in a different function, however, Xcode keeps saying there is an error.
I tried putting the function in different places, but it seems as though the function is not being read.
import Foundation
import UIKit
class BU: UIViewController {
let allBUSentences = BUSentenceBank()
var sentenceNumber2 : Int = 0
#IBOutlet weak var BUSentenceLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let BUfirstQuestion = allBUSentences.list2[sentenceNumber2]
BUSentenceLabel.text = BUfirstQuestion.BUSentenceText
}
#IBOutlet var BUNextButton: UIButton!
#IBAction func BUNext2(_ sender: Any) {
sentenceNumber2 = sentenceNumber2 + 1
nextSentence()
}
func nextSentence() {
if sentenceNumber2 <= 19 {
BUSentenceLabel.text = allBUSentences.list2[sentenceNumber2].BUSentenceText
}
else{
let alert2 = UIAlertController(title: "Game Over", message: "Restart and keep drinking?", preferredStyle: .alert)
let restartAction2 = UIAlertAction(title: "Restart", style: .default) { (UIAlertAction) in self.startover1()
}
alert2.addAction(restartAction2)
present(alert2, animated: true, completion: nil)
}
func startover1() {
sentenceNumber2 = 0
nextSentence()
}
}
}
Hopefully it will recognize the method. The error is Value of type 'BU' has no member 'startover1'
The problem is that nextSentence is nested inside another func declaration. To make it work simply remove the declaration to outside of the previous function scope defined by the {brackets}. Check out:
import Foundation
import UIKit
class BU: UIViewController {
let allBUSentences = BUSentenceBank()
var sentenceNumber2 : Int = 0
#IBOutlet weak var BUSentenceLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let BUfirstQuestion = allBUSentences.list2[sentenceNumber2]
BUSentenceLabel.text = BUfirstQuestion.BUSentenceText
}
#IBOutlet var BUNextButton: UIButton!
#IBAction func BUNext2(_ sender: Any) {
sentenceNumber2 = sentenceNumber2 + 1
nextSentence()
}
func nextSentence() {
if sentenceNumber2 <= 19 {
BUSentenceLabel.text = allBUSentences.list2[sentenceNumber2].BUSentenceText
}
else{
let alert2 = UIAlertController(title: "Game Over", message: "Restart and keep drinking?", preferredStyle: .alert)
let restartAction2 = UIAlertAction(title: "Restart", style: .default) { (UIAlertAction) in self.startover1()
}
alert2.addAction(restartAction2)
present(alert2, animated: true, completion: nil)
}
startover1() //This is not a declaration, but a call
}
//This is startover1 declaration
func startover1() {
sentenceNumber2 = 0
nextSentence()
}
}

Unresolved Identifier 'count'

Here is the error that I am seeing.
The "cardButton" is responsible for showing the next question. This is a small card app game that I am trying to make and as you can see from the image this is where I am having the issue with the code.
Here is the code:
import UIKit
class MultipleViewController: UIViewController {
#IBOutlet weak var questionLabel2: UILabel!
#IBOutlet var answerButtons: [UIButton]!
#IBOutlet weak var cardButton: UIButton!
#IBAction func cardButtonHandle(_ sender: Any) {
cardButton.isEnabled = true
if questionIdx < count(mcArray) - 1 { // There are still more questions
questionIdx += 1 //
} else {
questionIdx = 0
}
nextQuestion()
}
#IBAction func answerButtonHandle(_ sender: UIButton) {
if sender.titleLabel?.text == correctAnswer{
sender.backgroundColor = UIColor.green
print("Correct!")
} else {
sender.backgroundColor = UIColor.red
print("Wrong Answer")
}
for button in answerButtons{
button.isEnabled = false
if button.titleLabel?.text == correctAnswer {
button.backgroundColor = UIColor.green
}
}
cardButton.isEnabled = true // next question
}
var correctAnswer: String? // correct answers
var answers = [String]() // answers
var question : String? // Questions
var questionIdx = 0 // not sure what this is ?
override func viewDidLoad() {
super.viewDidLoad()
// titleForButtons() // call buttons as soon its loaded..
cardButton.isEnabled = false
nextQuestion()
}
func nextQuestion (){
let currentQuestion = mcArray![questionIdx]
answers = currentQuestion["Answers"] as! [String]
correctAnswer = currentQuestion["CorrectAnswer"] as? String
question = currentQuestion["Question"] as? String
titleForButtons ()
}
func titleForButtons (){
for (idx,button) in answerButtons .enumerated() {
button.titleLabel?.lineBreakMode = .byWordWrapping
button.setTitle(answers[idx],for:.normal)
button.isEnabled = true
}
questionLabel2.text = question
}
}
The following should work, you did not have the correct syntax for the length of the array. Note that if you have not initialized your questions array, this would cause a crash. Therefore you might want to add a guard into your code. Perhaps use the following
#IBAction func cardButtonHandle(_ sender: Any) {
cardButton.isEnabled = true
if questionIdx < (mcArray!.count) - 1 { // There are still more questions
questionIdx += 1 //
} else {
questionIdx = 0
}
nextQuestion()
}

How to keep track of items answered on in a label?

In my quiz app, if I wanted to keep track of the question number the user was on and display it, how would I do so with my following code? So if the user has answered two questions when the third appears I want it to display "question number 3". Basically I want the user to know what number question they are on and it should be equivalent to the number of questions they've answered plus one.
Here's my code:
import UIKit
class ViewController: UIViewController {
var questionList = [String]()
func updateCounter() {
counter -= 1
questionTimer.text = String(counter)
if counter == 0 {
timer.invalidate()
wrongSeg()
}
}
func randomQuestion() {
//random question
if questionList.isEmpty {
questionList = Array(QADictionary.keys)
}
let rand = Int(arc4random_uniform(UInt32(questionList.count)))
questionLabel.text = questionList[rand]
//matching answer values to go with question keys
var choices = QADictionary[questionList[rand]]!
questionList.remove(at: rand)
//create button
var button:UIButton = UIButton()
//variables
var x = 1
rightAnswerBox = arc4random_uniform(4)+1
for index in 1...4
{
button = view.viewWithTag(index) as! UIButton
if (index == Int(rightAnswerBox))
{
button.setTitle(choices[0], for: .normal)
}
else {
button.setTitle(choices[x], for: .normal)
x += 1
}
}
randomImage()
}
let QADictionary = ["Who is Thor's brother?" : ["Atum", "Loki", "Red Norvell", "Kevin Masterson"], "What is the name of Thor's hammer?" : ["Mjolinr", "Uru", "Stormbreaker", "Thundara"], "Who is the father of Thor?" : ["Odin", "Sif", "Heimdall", "Balder"]]
//wrong view segue
func wrongSeg() {
performSegue(withIdentifier: "incorrectSeg", sender: self)
}
//proceed screen
func rightSeg() {
performSegue(withIdentifier: "correctSeg", sender: self)
}
//variables
var rightAnswerBox:UInt32 = 0
var index = 0
//Question Label
#IBOutlet weak var questionLabel: UILabel!
//Answer Button
#IBAction func buttonAction(_ sender: AnyObject) {
if (sender.tag == Int(rightAnswerBox))
{
rightSeg()
timer.invalidate()
print ("Correct!")
}
if counter != 0 {
counter = 15
}
else if (sender.tag != Int(rightAnswerBox)) {
wrongSeg()
print ("Wrong!")
timer.invalidate()
questionList = []
}
}
override func viewDidAppear(_ animated: Bool)
{
randomQuestion()
questionTimer.text = String(counter)
timer = Timer.scheduledTimer(timeInterval: 1, target:self, selector: #selector(ViewController.updateCounter), userInfo: nil, repeats: true)
}
//variables
var counter = 15
var timer = Timer()
#IBOutlet weak var questionTimer: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
Take counter for total number of question globally and update it in random question like
var answerdQuestion = 1;
func randomQuestion() {
//random question
if questionList.isEmpty {
questionList = Array(QADictionary.keys)
}
lblQuestionNumber.text = Strint(answerdQuestion)
answerdQuestion += 1
:
:
}

How to make a timer reset after button gesture in a different view controller?

I am developing a quiz app where the user is asked a question at random and must answer it on the initial view controller. If the user picks correctly, a second view controller appears which contains a button that pops the view controller off the navigation stack and goes back to the initial view controller to finish the other questions. However, I have a timer that I want to reset (start at 15s) every time that second view controller is popped and the initial view controller appears with the next question. How would I accomplish this task? I already have the countdown timer code in my swift file. I just need to know how to get it to start from scratch every time the second view controller is popped/removed.
Here's my code for the initial view controller:
import UIKit
extension ViewController: QuizCompletedDelegate {
func continueQuiz() {
randomQuestion()
}
}
class ViewController: UIViewController {
var questionList = [String]()
func updateCounter() {
counter -= 1
questionTimer.text = String(counter)
if counter == 0 {
timer.invalidate()
wrongSeg()
counter = 15
}
}
func randomQuestion() {
//random question
if questionList.isEmpty {
questionList = Array(QADictionary.keys)
}
let rand = Int(arc4random_uniform(UInt32(questionList.count)))
questionLabel.text = questionList[rand]
//matching answer values to go with question keys
var choices = QADictionary[questionList[rand]]!
questionList.remove(at: rand)
//create button
var button:UIButton = UIButton()
//variables
var x = 1
rightAnswerBox = arc4random_uniform(4)+1
for index in 1...4 {
button = view.viewWithTag(index) as! UIButton
if (index == Int(rightAnswerBox)) {
button.setTitle(choices[0], for: .normal)
} else {
button.setTitle(choices[x], for: .normal)
x += 1
}
randomImage()
}
}
let QADictionary = ["Who is Thor's brother?" : ["Atum", "Loki", "Red Norvell", "Kevin Masterson"], "What is the name of Thor's hammer?" : ["Mjolinr", "Uru", "Stormbreaker", "Thundara"], "Who is the father of Thor?" : ["Odin", "Sif", "Heimdall", "Balder"]]
//wrong view segue
func wrongSeg() {
performSegue(withIdentifier: "incorrectSeg", sender: self)
}
//proceed screen
func rightSeg() {
performSegue(withIdentifier: "correctSeg", sender: self)
}
//variables
var rightAnswerBox:UInt32 = 0
var index = 0
//Question Label
#IBOutlet weak var questionLabel: UILabel!
//Answer Button
#IBAction func buttonAction(_ sender: AnyObject) {
if (sender.tag == Int(rightAnswerBox)) {
rightSeg()
print ("Correct!")
}
if counter != 0 {
counter = 15
questionTimer.text = String(counter)
} else if (sender.tag != Int(rightAnswerBox)) {
wrongSeg()
print ("Wrong!")
timer.invalidate()
questionList = []
}
}
override func viewDidAppear(_ animated: Bool) {
randomQuestion()
}
//variables
var counter = 15
var timer = Timer()
#IBOutlet weak var questionTimer: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
timer = Timer.scheduledTimer(timeInterval: 1, target:self, selector: #selector(ViewController.updateCounter), userInfo: nil, repeats: true)
}
}
Here's the code to the second view controller:
class ContinueScreen: UIViewController {
var delegate: QuizCompletedDelegate?
//correct answer label
#IBOutlet weak var correctLbl: UILabel!
//background photo
#IBOutlet weak var backgroundImage: UIImageView!
func backToQuiz() {
delegate?.continueQuiz()
if let nav = self.navigationController {
nav.popViewController(animated: true)
} else {
self.dismiss(animated: true, completion: nil)
}
}
#IBAction func `continue`(_ sender: Any) {
backToQuiz()
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
How to make countdown timer reset after button push on different view
controller?
So if you can get the reference of yourView controller where you have
setup the timer then you can access counter variable from previous
view controller inside your second viewController and when pushed the
button just reset to 0.
If above approach is not possible for you then another way is create
the static variable and access it anywhere and reset like this:-
static var counter = 0
if you are popping/removing the 2nd view controller and thus returning to the first, you could reset the timer in your viewDidAppearfunc in your 1st view controller. this way the timer restarts every time the view appears - the first time, and whenever the 2nd pops.

save a highscore with UserDefaults in Swift 3 [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
class ThirdViewController: UIViewController {
var i = 3
var a: Double = 0
var fastestScore:Float = 100000000000000.0
var randomNumberX = Int(arc4random_uniform(375))
var randomNumberY = Int(arc4random_uniform(667))
#IBOutlet weak var fastestScoreLabel: UILabel!
#IBOutlet weak var homeButtonLabel: UIButton!
#IBOutlet weak var finalLabel: UILabel!
#IBOutlet weak var playAgainButtonLabel: UIButton!
#IBOutlet weak var circleImage: UIButton!
#IBOutlet weak var secondTimerLabel: UILabel!
#IBOutlet weak var counterLabel: UILabel!
var timerA = Timer()
var timerB = Timer()
#IBAction func homeButton(_ sender: Any) {
nextPage = false
}
#IBAction func playAgainButton(_ sender: Any) {
randomNumberX = Int(arc4random_uniform(375))
randomNumberY = Int(arc4random_uniform(667))
homeButtonLabel.isHidden = true
counterLabel.text = "3"
i = 3
timerA = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(ThirdViewController.counterFunc), userInfo: nil, repeats: true)
a = 0
counterLabel.isHidden = false
playAgainButtonLabel.isHidden = true
circleImage.isHidden = true
finalLabel.isHidden = true
secondTimerLabel.isHidden = true
fastestScoreLabel.isHidden = true
}
#IBAction func circleButton(_ sender: Any) {
fastestScoreLabel.isHidden = false
circleImage.isHidden = true
homeButtonLabel.isHidden = false
timerB.invalidate()
playAgainButtonLabel.isHidden = false
var saying = ""
if a < 0.2 {
saying = "That's actually pretty good \(a) seconds is pretty fast"
} else if a >= 0.2 && a <= 0.45 {
saying = "\(a) seconds is not really that good"
} else {
saying = "\(a) seconds? Seriously? Are you even trying?"
}
finalLabel.text = saying
finalLabel.isHidden = false
if Float(a) < fastestScore {
fastestScore = Float(a)
UserDefaults.standard.set(fastestScore, forKey: "Fastest Score")
let savedFastestScore = UserDefaults.standard.float(forKey: "Fastest Score")
fastestScoreLabel.text = String(savedFastestScore)
}
}
func secondTimer() {
a = a + 0.01
secondTimerLabel.text = String(a)
}
func counterFunc() {
if i > 1 {
i -= 1
counterLabel.text = String(i)
} else {
timerB = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(ThirdViewController.secondTimer), userInfo: nil, repeats: true)
circleImage.isHidden = false
self.circleImage.center = CGPoint(x:randomNumberX, y:randomNumberY)
counterLabel.isHidden = true
timerA.invalidate()
secondTimerLabel.isHidden = false
}
}
override func viewDidLoad() {
super.viewDidLoad()
fastestScoreLabel.isHidden = true
homeButtonLabel.isHidden = true
playAgainButtonLabel.isHidden = true
circleImage.isHidden = true
secondTimerLabel.isHidden = true
timerA = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(ThirdViewController.counterFunc), userInfo: nil, repeats: true)
// 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 prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
I have tried many different times using userDefaults to try and save the highscore when you close out but I just can't seem to do it. If someone has any idea on how to do it I would really appreciate it lol.
You still haven't clearly explained what problem you're seeing, but my guess is that your high score doesn't appear to be saved across restarts of the app.
The problem is that you initialize fastestScore to 100000000000000.0 when your ThirdViewController is created. You don't load it from UserDefaults. Thus even if there is a stored fastestScore, you don't load it at launch.
You should fix this with two changes. First, in your application delegate, you should register a default high score:
let bestScoreKey = "bestScore"
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, willFinishLaunchingWithOptions options: [UIApplicationLaunchOptionsKey : Any]?) -> Bool {
UserDefaults.standard.register(defaults: [
bestScoreKey: 1_000_000_000.0
])
return true
}
}
You should also register any other default settings there.
Second, in ThirdViewController, you should initialize fastestScore from UserDefaults. You can also save changes back to UserDefaults in the property's didSet observer:
var fastestScore: Double = UserDefaults.standard.double(forKey: bestScoreKey) {
didSet {
UserDefaults.standard.set(fastestScore, forKey: bestScoreKey)
}
}
Other tips:
There is no reason to make fastestScore a Float if your scores are Double. Just save it as a Double.
Don't repeat string keys. The compiler won't catch your spelling errors. Put the key in a constant like I did with bestScoreKey.
You can use _ in long numbers to make them more readable.
Insulting your player is a questionable marketing strategy. How would you have felt if the first comment on your question was “Seriously? Are you even trying?”
When you are saving high scores, make sure that the current score you have is greater than the new score.
suppose the current high score is 100
var fastestScore = 100
var currentScore: Int?
if currentScore < fastestScore {
Userdefaults.standard.set(currentScore, forKey: "Fastest Score")
}
The code above will go to the function where you need to save the high score. Maybe in your game over scene.
To retrieve the low score, do this
var savedFastestScore = UserDefaults.standard.float(forKey: "Fastest Score")