Hi,
I might be breaking some rule with my message but please excuse me for my first time login!
After months of studying swift, I thought I'm able to start my first simple app! But seems the reality is different from my thoughts!
What I need to do is a testing app, the interface has 3 buttons, the app will ask a question e.g. 3x6, the right answer goes to one of the buttons in random order, while the other buttons will be filled with other random numbers!
I tried to do it by taking two numbers from two random generators (num1 and num2), take these two numbers to say(3 and 6) and display it in one of the buttons. At this point where I got messing around, first, how to take the last two numbers and fill it in remaining buttons, second, I have a vague idea is to use a loop which representing the buttons where I got lost when I tried it :(
I have struggled with this problem for a few weeks trying different approaches but at the end, I go to the same problems which I mentioned above.
It can be helpful when asking questions to post the code you do have, even if it doesnât work. It helps the rest of the world understand the question.
But since you havenât, Iâll just post what I would write if I were trying to accomplish that. Hopefully the answer to your question is in there somewhere. For the sake of your own satisfaction, donât just copy and paste what I did. Learn from it, but then stick with what you have and adjust it until it works. If you have Xcode, you can watch the following execute by pasting it into a playground:
/// Defines the highest factor we want to ask about.
/// (Since 45 645 648 906 584 Ă 456 048 564 485 would probably be cruel.)
let maximumFactor = 10
/// The highest product that will ever be correct.
let maximumProduct = maximumFactor * maximumFactor
/// A multiple choiceâoption.
struct MultipleChoiceOption {
/// The number to display as the option.
let number: Int
/// Whether or not the option is the correct one.
let isCorrect: Bool
}
/// A multiplication question.
struct MultiplicationQuestion {
/// The multiplier (first factor).
let multiplier: Int
/// The multiplicand (second factor).
let multiplicand: Int
/// The product (answer).
let product: Int
/// The options to choose from.
let options: [MultipleChoiceOption]
/// Creates a question from two factors.
init(multiplier: Int, multiplicand: Int) {
self.multiplier = multiplier
self.multiplicand = multiplicand
let product = multiplier * multiplicand
self.product = product
var options: [MultipleChoiceOption] = []
let correct = MultipleChoiceOption(number: product, isCorrect: true)
options.append(correct)
while options.count < 3 { // Keep adding incorrect answers until there are 3.
let random = Int.random(in: 0 ... maximumProduct)
if !options.contains(where: { $0.number == random }) { // We donât want duplicates.
let incorrect = MultipleChoiceOption(number: random, isCorrect: false)
options.append(incorrect)
}
}
self.options = options.shuffled()
}
/// Creates a random question (limited by `maximumFactor`).
static func random() -> MultiplicationQuestion {
let multiplier = Int.random(in: 0 ... maximumFactor)
let multiplicand = Int.random(in: 0 ... maximumFactor)
return MultiplicationQuestion(multiplier: multiplier, multiplicand: multiplicand)
}
}
// Say there was an array of 3 buttons somewhere else like this, but with buttons not text:
let buttons: [Any] = ["Button", "Button", "Button"]
/// Displays a question.
func ask(_ question: MultiplicationQuestion) {
// This just prints it as text output.
// Your application will need to display these in UI elements.
print("\(question.multiplier) Ă \(question.multiplicand)")
print("")
for (option, button) in zip(question.options, buttons) {
// You can use the corresponding `button` here,
// but since ours are just dummies, we wonât.
print("⢠\(option.number)?")
}
}
let question = MultiplicationQuestion.random()
ask(question)
print("")
/// This checks the answer.
///
/// Interface buttons will have to call it with their respective index.
func answer(_ question: MultiplicationQuestion, withOption optionIndex: Int) {
let option = question.options[optionIndex]
print(option.number)
print("")
if option.isCorrect {
print("\(question.multiplier) Ă \(question.multiplicand) = \(option.number) â")
} else {
print("\(question.multiplier) Ă \(question.multiplicand) â \(option.number) â")
print("\(question.multiplier) Ă \(question.multiplicand) = \(question.product)")
}
}
// Buttons would do the following:
// (But because the playground doesnât have buttons, weâll always answer with the first one.)
answer(question, withOption: 0)
I really appreciated your effort to send me this code which really explains my needs and I'm sorry to reply you a bit late due to certain circumstances I have in the last few weeks!
I went through the code and I had few issues. first, "type 'Int' has no member 'random'", in any of the following instruction with a fashion of e.g.
let multiplicand = Int.random(in: 0 ... maximumFactor)"
I have to change it to let multiplicand = Int(arc4random_uniform( UInt32(maximumFactor))), however, hope my change is right :)
Two things left for me:
1- let random = Int(arc4random_uniform( UInt32(maximumProduct))) which gave me this warning: "Initialization of immutable value 'random' was never used; consider replacing with assignment to '_' or removing it". I know because there no other place has random to use!
2-****self.options = options.shuffled() "Value of type '[MultipleChoiceOption]' has no member 'shuffled'"
Thanks, Jeremy! Yep, my version was 4.1 and Xcode was 9+. Both now updated and the playground worked ok.
I will create a new project and see how it goes :)
I opened a new project in order to translate your code into it, I got this error: "Cannot use instance member 'maximumFactor' within property initializer; property initializers run before 'self' is available" for this instruction**: **let maximumProduct = maximumFactor * maximumFactor!
Does that mean I have to use it within a function or else?
I followed some stackoverview solutions, to be honest, I got lots, I think this is out of my knowledge at this stage :)
If possible can you please explain what is happening?
The following are three minimal examples to illustrate what I think is going on. Note the slight differences in where and how maximumFactor is declared and used.
What I originally wrote:
// These are in the global scope,
// so they are global constants.
let maximumFactor = 10
let maximumProduct = maximumFactor * maximumFactor
// â Global constants can reference each other,
// (but not circularly).
struct MultiplicationQuestion {
// These are inside a type declaration,
// so they are instance properties,
// which means each question instance stores a separate one.
let maximumFactor = 10
let maximumProduct = maximumFactor * maximumFactor
// â Instance properties cannot reference each other.
// As the error says,
// âproperty initializers run before 'self' is availableâ,
// meaning when they are computed, the associated instance question
// does not exist yet for them to ask it about its other properties.
}
For more details see The Swift Programming Language under âStored Propertiesâ.
An alternative you may be looking for:
struct MultiplicationQuestion {
// These use the keyword âstaticâ,
// which makes them belong to the type itself,
// not to any particular instance.
// Static properties work much like a global constants,
// but they are namespaced under the specific type,
// in this case âMultiplicationQuestionâ.
static let maximumFactor = 10
static let maximumProduct = maximumFactor * maximumFactor
// â Global constants can reference each other,
// (but not circularly).
}
// If you want to use static properties from a different scope,
// you have to use their namespace.
let x = MultiplicationQuestion.maximumFactor
For more details see The Swift Programming Language under âType Propertiesâ.
I have spent too much time trying to bring the code to run but I faced many problems! So, I thought to simulate the idea with the following code which it did, it is primitive to use! I tried to implement with your code in order to display the question lbl.text = ("(question.multiplier) Ă (question.multiplicand)") unfortunately it didn't work, while on following code it works fine. I have no idea what I'm doing wrong! Your help is much appreciated
The Code:
import UIKit
class ViewController: UIViewController {
let questions = ["8*9", "6*6", "3*8"]
let answers = [["72", "36", "24"], ["36", "56", "49"], ["24", "54", "63"]]
var currentQuestion = 0
var correctAnswer = 0
@IBOutlet weak var lbl: UILabel! //for questions
@IBAction func action(_ sender: AnyObject) {
if (sender.tag == Int(correctAnswer)){
print("correctAnswer= \(correctAnswer)")
print("Right")
}
else{
print("Wrong")
}
if currentQuestion != questions.count {
print("currentQuestion= \(currentQuestion)")
print("questions.count= \(questions.count)")
nextQuestion()
if currentQuestion == questions.count{
currentQuestion = 0 // back again
}
}
}
override func viewDidAppear(_ animated: Bool){
nextQuestion()
}
//Function that displays new question
func nextQuestion(){
lbl.text = questions[currentQuestion]
correctAnswer = Int.random(in: 0...2)
var button : UIButton = UIButton()
var x = 1
for i in 0...2
{
button = view.viewWithTag(i) as! UIButton
if (i == Int(correctAnswer)){
button.setTitle(answers[currentQuestion][0], for: .normal)
}
else{
button.setTitle(answers[currentQuestion][x], for: .normal)
x = 2
}
}
currentQuestion += 1 // go to next Question
}
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.
}
I have too little information about what you actually have set up right now. If you want more help, you will have to copy and paste the exact error message or take a screenshot of the unexpected result.
If you think the problem has more to do with CocoaTouch (such as UILabel and @IBAction) than it has to do with the Swift language itself (such as func and random), you could also ask for help over at Appleâs Developer Forums.