2

I do not have any experience with programing. I have done this looking at youtube videos for a couple of months. I will really appreciate if someone can please help me. When I run the code with the simulator it repeats the questions several time before the next new question is presented. I would like it to run so it presents one question without repeating the same question over and over. Below please find the code.

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var QuestionLabel: UILabel!

    @IBOutlet weak var Button1: UIButton!

    @IBOutlet weak var Button2: UIButton!

    @IBOutlet weak var Button3: UIButton!

    @IBOutlet weak var Button4: UIButton!

    @IBOutlet weak var Next: UIButton!

    @IBOutlet weak var LabelEnd: UILabel!
    var CorrectAnswer = String()


    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        Hide()
        RamdomQuestions()
    }

    func RamdomQuestions () {
        var RandomNumber = arc4random() % 4
        RandomNumber += 1

        switch (RandomNumber) {

        case 1:
            QuestionLabel.text = "Hola familia, Cual es mi nombre? "
            Button1.setTitle ("Cesar", forState: UIControlState.Normal)
            Button2.setTitle ("Karlos", forState: UIControlState.Normal)
            Button3.setTitle ("William", forState: UIControlState.Normal)
            Button4.setTitle ("Chiqui", forState: UIControlState.Normal)
            CorrectAnswer = "2"

            break
        case 2:
            QuestionLabel.text = "Hola famili, cual es mi apellido? "
            Button1.setTitle ("Perez", forState: UIControlState.Normal)
            Button2.setTitle ("Carvajal", forState: UIControlState.Normal)
            Button3.setTitle ("Garcia", forState: UIControlState.Normal)
            Button4.setTitle ("Sanchez", forState: UIControlState.Normal)
            CorrectAnswer = "1"

            break
        case 3:
            QuestionLabel.text = "Quien hace la lachona mas rica? "
            Button1.setTitle ("Willy", forState: UIControlState.Normal)
            Button2.setTitle ("Mario", forState: UIControlState.Normal)
            Button3.setTitle ("Karlos", forState: UIControlState.Normal)
            Button4.setTitle ("Juan David", forState: UIControlState.Normal)
            CorrectAnswer = "1"

            break
        case 4:
            QuestionLabel.text = "Quien hace las tartas mas lindas"
            Button1.setTitle ("Jili", forState: UIControlState.Normal)
            Button2.setTitle ("Carvajal", forState: UIControlState.Normal)
            Button3.setTitle ("Garcia", forState: UIControlState.Normal)
            Button4.setTitle ("Leidy y Liz", forState: UIControlState.Normal)
            CorrectAnswer = "4"

            break

        default:
            break
        }
    }

    func Hide (){
        LabelEnd.hidden = true
        Next.hidden = true
    }

    func UnHide () {
        LabelEnd.hidden = false
        Next.hidden = false
    }

    @IBAction func Button1Action(sender: AnyObject) {
        UnHide()
        if (CorrectAnswer == "1") {
            LabelEnd.text = "Correcto"
        }
        else{
            LabelEnd.text = "Falso"
        }
    }

    func Button2Action(sender: AnyObject) {
        UnHide()
        if (CorrectAnswer == "2") {
            LabelEnd.text = "Correcto"

        }
        else{
            LabelEnd.text = "Falso"
        }
    }

    func Button3Action(sender: AnyObject) {
        UnHide()
        if (CorrectAnswer == "3") {
            LabelEnd.text = "Correcto"
        }
        else{
            LabelEnd.text = "Falso"
        }
    }

    func Button4Action(sender: AnyObject) {
        UnHide()
        if (CorrectAnswer == "4") {
            LabelEnd.text = "Correcto"
        }
        else{
            LabelEnd.text = "Falso"
        }
    }

    @IBAction func Next(sender: AnyObject) {
        RamdomQuestions()
    }
}
srvv
  • 613
  • 4
  • 10
C.Sanchez
  • 25
  • 1
  • 7
  • Store the last Random number and check every time if current random number is not equal to last random number – Saurabh Prajapati Jun 11 '16 at 04:08
  • Or create question class with your question and answers. Then create an array of question class instances, pick a random number between O and total count of values -1. The show that question at that random index, and remove it from the array, once answered, repeat the process. – AntonTheDev Jun 11 '16 at 05:30
  • Have you tried the code which I have provided in my answer? – Ashish Verma Jun 11 '16 at 19:29

3 Answers3

2

Try the following code, hope it will work for you

class ViewController: UIViewController {

@IBOutlet weak var QuestionLabel: UILabel!
@IBOutlet weak var Button1: UIButton!
@IBOutlet weak var Button2: UIButton!
@IBOutlet weak var Button3: UIButton!
@IBOutlet weak var Button4: UIButton!
@IBOutlet weak var Next: UIButton!
@IBOutlet weak var LabelEnd: UILabel!

var CorrectAnswer = String()
var randomQuestionArray:[Int] = [1, 2, 3, 4]

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

    Hide()
    RamdomQuestions()
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

func RamdomQuestions () {

    let randomIndex = Int(arc4random_uniform(UInt32(randomQuestionArray.count)))

    if randomQuestionArray.count > 0 {

    switch (randomQuestionArray[randomIndex]) {

    case 1:
        QuestionLabel.text = "Hola familia, Cual es mi nombre? "
        Button1.setTitle ("Cesar", forState: UIControlState.Normal)
        Button2.setTitle ("Karlos", forState: UIControlState.Normal)
        Button3.setTitle ("William", forState: UIControlState.Normal)
        Button4.setTitle ("Chiqui", forState: UIControlState.Normal)
        CorrectAnswer = "2"
        break

    case 2:
        QuestionLabel.text = "Hola famili, cual es mi apellido? "
        Button1.setTitle ("Perez", forState: UIControlState.Normal)
        Button2.setTitle ("Carvajal", forState: UIControlState.Normal)
        Button3.setTitle ("Garcia", forState: UIControlState.Normal)
        Button4.setTitle ("Sanchez", forState: UIControlState.Normal)
        CorrectAnswer = "1"
        break

    case 3:
        QuestionLabel.text = "Quien hace la lachona mas rica? "
        Button1.setTitle ("Willy", forState: UIControlState.Normal)
        Button2.setTitle ("Mario", forState: UIControlState.Normal)
        Button3.setTitle ("Karlos", forState: UIControlState.Normal)
        Button4.setTitle ("Juan David", forState: UIControlState.Normal)
        CorrectAnswer = "1"
        break

    case 4:
        QuestionLabel.text = "Quien hace las tartas mas lindas"
        Button1.setTitle ("Jili", forState: UIControlState.Normal)
        Button2.setTitle ("Carvajal", forState: UIControlState.Normal)
        Button3.setTitle ("Garcia", forState: UIControlState.Normal)
        Button4.setTitle ("Leidy y Liz", forState: UIControlState.Normal)
        CorrectAnswer = "4"
        break

    default:
        break

    }
        randomQuestionArray.removeAtIndex(randomIndex)
    }


}

func Hide () {
    LabelEnd.hidden = true
    Next.hidden = true
}

func UnHide () {
    LabelEnd.hidden = false
    Next.hidden = false
}

@IBAction func Button1Action(sender: AnyObject) {
    UnHide()
    if (CorrectAnswer == "1") {
        LabelEnd.text = "Correcto"
    } else{
        LabelEnd.text = "Falso"
    }
}

func Button2Action(sender: AnyObject) {
    UnHide()
    if (CorrectAnswer == "2") {
        LabelEnd.text = "Correcto"
    } else{
        LabelEnd.text = "Falso"
    }
}

func Button3Action(sender: AnyObject) {
    UnHide()
    if (CorrectAnswer == "3") {
        LabelEnd.text = "Correcto"
    } else{
        LabelEnd.text = "Falso"
    }
}

func Button4Action(sender: AnyObject) {
    UnHide()
    if (CorrectAnswer == "4") {
        LabelEnd.text = "Correcto"
    } else{
        LabelEnd.text = "Falso"
    }
}

@IBAction func Next(sender: AnyObject) {
    RamdomQuestions()
}

}
Ashish Verma
  • 1,776
  • 1
  • 16
  • 27
  • Dear Ashish, How are you? Thank you very much for your answer, time and help. I do appreciate it a lot. Are you from India? Do you live in the U.S? I tried the changes you provided but I am still have two errors. Would you please take a look at it and see if you can please help me again? I will really appreciate it. Regards, Cesar – C.Sanchez Jun 14 '16 at 02:58
  • 1. ramdomQuestionArray = [1, 2, 3, 4] is giving me an error that says. "use of unresolved identifier 'randomQuestionArray' 2. var randomIndex = Int(arc4random_uniform(randomQuestionArray.count)) is giving me an error saying "cannot convert value of type 'Int' to expected argument type 'UInt32' – C.Sanchez Jun 14 '16 at 03:13
  • 3. randomQuestionArray.removeAtIndex(index) is giving me an error saying "cannot convert value of type '(UnsafePointer,Int32)->UnsafeMutablePointer'to expected argument type 'Int' Can you please take a look at them and please let me know how to fix them? I will really appreciate it – C.Sanchez Jun 14 '16 at 03:14
  • Hi @C.Sanchez , I am good and I am from India. I have updated my answer please check now, if it works for you or not. – Ashish Verma Jun 14 '16 at 05:21
  • Ceaser, have you tried the code that I have provided in my answer? – Ashish Verma Jun 15 '16 at 02:27
  • Dear Ashish, It did work. Thank you very much for your time and support. I do appreciate it. – C.Sanchez Jun 15 '16 at 02:44
  • On left side of my answer, you will see a highlighted tick mark. Select that tick mark and it will turn green. This indicates that my answer was useful to you. – Ashish Verma Jun 15 '16 at 03:03
  • I would like to send you a new version of the code to ask you one more question. How do I post a new version? – C.Sanchez Jun 15 '16 at 03:07
  • Can you please help me? I will appreciate it. – C.Sanchez Jun 15 '16 at 03:08
  • Post another question – Ashish Verma Jun 15 '16 at 03:09
1

The typical solution for this is to have an array of questions (or indexes into your model) and then shuffle this array so it's random. You can then iterate through this shuffled array of questions, and they'll appear in a largely random fashion, but you don't have to worry about them reappearing.

In Swift 4.2, you'd use the built-in shuffle or shuffled methods to shuffle your array.


In Swift versions before 4.2, you have to shuffle the array yourself. Note, when generating a random number, you should not use arc4random with the % operator. That introduces modulo bias. Instead, use arc4random_uniform which generates uniformly distributed random numbers within a range of values. And shuffling the array, you should use a Fisher-Yates algorithm, which eliminates some subtle biases introduced by naive shuffling algorithms. For general information, see the Fisher-Yates article in Wikipedia. For specific Swift implementation, see How do I shuffle an array in Swift?. Anyway, the algorithm looks like:

extension MutableCollection {

    /// Shuffle the elements of `self` in-place.

    mutating func shuffle() {
        if count < 2 { return }    // empty and single-element collections don't shuffle

        for i in 0 ..< count - 1 {
            let j = Int(arc4random_uniform(UInt32(count - i)))
            if j != 0 {
                let current = index(startIndex, offsetBy: i)
                let swapped = index(current, offsetBy: j)
                swapAt(current, swapped)
            }
        }
    }

    /// Return shuffled collection the elements of `self`.

    func shuffled() -> Self {
        var results = self
        results.shuffle()
        return results
    }

}

You can then use it like so:

var questionIndexes = Array(0 ..< questions.count)  // builds an array [0, 1, 2, ... n-1], where _n_ is the number of questions
questionIndexes.shuffle()                           // shuffle that list

You end up with an array of the numbers 0 through n-1 that are shuffled (i.e. appear in a random order, but no number occurs more than once). You can now iterate through this array of questionIndexes and each question will be asked once and only once, but they'll be presented in random order.


A couple of unrelated observations:

  1. You probably want to adopt Cocoa naming conventions, namely that method and property names should always start with a lowercase letter. Only data types (e.g. classes or structs) and enums start with uppercase letters.

  2. When doing a switch statement in Swift, you don't need break. Swift doesn't fall through like Objective-C does. When I tackled point 5, below, it turns out I ended up factoring out the switch statement entirely, but just for your future reference, you don't need to break at the end of each case in Swift like you did in C-based programming languages like Objective-C. In fact, if you want a Swift case statement to fall through to the next one, like it does in Objective-C, you'd have to use the fallthrough keyword.

  3. You probably shouldn't initialize correctAnswer as String(). Just declare it to be an optional (an implicitly unwrapped one, if you want).

  4. Even better, correctAnswer should be an Int rather than a String. I'd also use a zero-based value so that I can easily look up the value to confirm whether the right button was pressed.

  5. This is a more advanced topic, but I'd suggest separating the "model" (i.e. the data about question text, potential answers, and correct answer) from "controller" (the code that takes information from the model and updates the view). This is part of the model-view-controller paradigm that we use in our apps. It makes your app easier to maintain in the future (e.g. you can add more questions, change questions, etc., but not have to touch the code in the view controller). It also enables more flexible patterns (e.g. the questions and answers could be provided by a remote web service or be stored in a database).

    For example, you might have a type that captures the question, its potential answer, and identifies which is the correct answer.

    struct Question {
        let question: String
        let answers: [String]
        let correctAnswer: Int
    }
    

    Your model might then consist of an array of Question objects:

    var questions: [Question] = [
        Question(
            question: "Hola familia, Cual es mi nombre?",
            answers: ["Cesar", "Karlos", "William", "Chiqui"],
            correctAnswer: 1),
        Question(
            question: "Hola famili, cual es mi apellido?",
            answers: ["Perez", "Carvajal", "Garcia", "Sanchez"],
            correctAnswer: 0),
        Question(
            question: "Quien hace la lachona mas rica?",
            answers: ["Willy", "Mario", "Karlos", "Juan David"],
            correctAnswer: 2),
        Question(
            question: "Quien hace las tartas mas lindas?",
            answers: ["Jili", "Carvajal", "Garcia", "Leidy y Liz"],
            correctAnswer: 3)
    ]
    

    Note, forgive my changing the "correct answer" in these questions from that which was presented in your question. I just wanted to illustrate that we're dealing with numbers from 0 through 3 (not 1 through 4).


Pulling this all together, you might have an implementation that looks like:

import UIKit

struct Question {
    let question: String
    let answers: [String]
    let correctAnswer: Int
}

class ViewController: UIViewController {

    @IBOutlet weak var questionLabel: UILabel!

    @IBOutlet weak var button1: UIButton!
    @IBOutlet weak var button2: UIButton!
    @IBOutlet weak var button3: UIButton!
    @IBOutlet weak var button4: UIButton!

    lazy var buttons: [UIButton] = { return [self.button1, self.button2, self.button3, self.button4] }()

    @IBOutlet weak var nextButton: UIButton!

    @IBOutlet weak var endLabel: UILabel!

    var questions: [Question] = [
        Question(
            question: "Hola familia, Cual es mi nombre?",
            answers: ["Cesar", "Karlos", "William", "Chiqui"],
            correctAnswer: 1),
        Question(
            question: "Hola famili, cual es mi apellido?",
            answers: ["Perez", "Carvajal", "Garcia", "Sanchez"],
            correctAnswer: 0),
        Question(
            question: "Quien hace la lachona mas rica?",
            answers: ["Willy", "Mario", "Karlos", "Juan David"],
            correctAnswer: 2),
        Question(
            question: "Quien hace las tartas mas lindas?",
            answers: ["Jili", "Carvajal", "Garcia", "Leidy y Liz"],
            correctAnswer: 3)
    ]

    var questionIndexes: [Int]!
    var currentQuestionIndex = 0

    override func viewDidLoad() {
        super.viewDidLoad()

        questionIndexes = Array(0 ..< questions.count)  // builds an array [0, 1, 2, ... n]
        questionIndexes.shuffle()                       // randomizes that list

        updateLabelsAndButtonsForIndex(0)
    }

    func updateLabelsAndButtonsForIndex(questionIndex: Int) {
        // if we're done, show message in `endLabel` and hide `nextButton`

        guard questionIndex < questions.count else {
            endLabel.hidden = false
            endLabel.text = "All done!"
            nextButton.hidden = true
            return
        }

        // update our property

        currentQuestionIndex = questionIndex

        // hide end label and next button

        hideEndLabelAndNextButton()

        // identify which question we're presenting

        let questionObject = questions[questionIndexes[questionIndex]]

        // update question label and answer buttons accordingly

        questionLabel.text = questionObject.question
        for (answerIndex, button) in buttons.enumerate() {
            button.setTitle(questionObject.answers[answerIndex], forState: .Normal)
        }
    }

    func hideEndLabelAndNextButton() {
        endLabel.hidden = true
        nextButton.hidden = true
    }

    func unhideEndLabelAndNextButton() {
        endLabel.hidden = false
        nextButton.hidden = false
    }

    // note, because I created that array of `buttons`, I now don't need
    // to have four `@IBAction` methods, one for each answer button, but 
    // rather I can look up the index for the button in my `buttons` array
    // and see if the index for the button matches the index of the correct
    // answer.

    @IBAction func didTapAnswerButton(button: UIButton) {
        unhideEndLabelAndNextButton()

        let buttonIndex = buttons.indexOf(button)
        let questionObject = questions[questionIndexes[currentQuestionIndex]]

        if buttonIndex == questionObject.correctAnswer {
            endLabel.text = "Correcto"
        } else {
            endLabel.text = "Falso"
        }
    }

    @IBAction func didTapNextButton(sender: AnyObject) {
        updateLabelsAndButtonsForIndex(currentQuestionIndex + 1)
    }

}

There's a lot buried in there, so I wouldn't worry about the details if you don't quite follow everything that's going on here. But the key is, build an array of indexes into your model, shuffle it, and then you can proceed to iterate through this shuffled array and you're guaranteed that you won't ask the same question twice.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • Dear Rob, I thank you so much for taking the time to provide such a detailed explanation for my question. I do appreciate it a lot. I will try it and get back to you soon. Thank you. – C.Sanchez Jun 15 '16 at 02:31
  • Dear Rob, I am trying your changes. I will get back to you tomorrow with the response. Thank you so much for your detailed explanation. I do appreciate a lot. Regards, Cesar! – C.Sanchez Jun 16 '16 at 03:35
  • Dear Rob, I completed the changes that you made to the code and when I run the simulator it opens up. However, I cannot see the app and it brings me to the AppDelegate.Swift with the following error message: Class AppDelegate: UIResponder, UIApplicationDelegate {Thread 1: signal SIGABRT. Can you please let me know what I did wrong? I will really appreciate it. Regards, Cesar – C.Sanchez Jun 17 '16 at 03:02
  • Dear Rob, Can you please tell me how can I download Cocoa? I went to the app store but I do not see it. Thank you for your time and patience. I am new with mac and I am trying to learn swift. – C.Sanchez Jun 17 '16 at 03:21
  • @C.Sanchez Regarding the SIGABRT, you have to identify the source of the crash. Try exception breakpoint. Or look at stack trace. Or add a bunch of `print` statements. But it's impossible to say on the basis of so little information. Usually the console would give you more descriptive error messages. – Rob Jun 17 '16 at 03:34
  • @C.Sanchez Re "how can I download Cocoa?", I don't know what you mean. Cocoa Touch _is_ what you're using already. It's the name of the iOS API that we all use when writing apps. – Rob Jun 17 '16 at 03:36
  • Dear Rob, I am still trying to make the code work. I connected @IBOutlet questionLabel to a label on the main.storyboard. – C.Sanchez Jun 20 '16 at 03:36
  • There really isn't anything else here. You just need to make sure you hook up _all_ of the outlets (e.g. outlets to question label, end label, the four answer buttons and the next button, and also hook up `@IBAction` for all five buttons, too. See https://github.com/robertmryan/Quiz for example of everything hooked up. – Rob Jun 20 '16 at 18:51
0

I connected initially the

@IBAction func didTapAnswerButton(button:

and the

@IBAction func didTapNextButton(sender:

to the five buttons and the simulator was not showing the correct or false on the screen.

I tried one more time, just connecting the

@IBAction func didTapAnswerButton(button:

to the five buttons and the

@IBAction func didTapNextButton(sender:

to the Next button and it worked.

Pang
  • 9,564
  • 146
  • 81
  • 122
C.Sanchez
  • 25
  • 1
  • 7