Swift 4 - Quiz App Random Questions - ios

I'm a beginner to Swift and generally Xcode. Thank you in advance for your time and help :)
I am doing a Quiz App, and I try to have random questions... I have a function to generate random numbers that I tried to apply in viewDidLoad()... unfortunately I can not guess how to use that information in his variable "var currentQuestion = 0"
This is the code:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
generateRandomNumber(0, 9, 10)
}
override func viewDidAppear(_ animated: Bool) {
newQuestion()
}
// Quiz//
let questions = ["Q1","Q2","Q3","Q4","Q5"]
let answers = [["A","2","3"],["B","2","3"],["C","2","3"],["D","2","3"],["E","2","3"]]
//Variables
var currentQuestion = 0
var rightAnswerPlacement:UInt32 = 0
//Generate Random Numbers
func generateRandomNumber(_ from:Int, _ to:Int, _ qut:Int?) -> [Int] {
var myRandomNumbers = [Int]() //All our generated numbers
var numberOfNumbers = qut //How many numbers to generate
let lower = UInt32(from) //Generate from this number..
let higher = UInt32(to+1) //To this one
if numberOfNumbers == nil || numberOfNumbers! > (to-from) + 1 {
numberOfNumbers = (to-from) + 1
}
while myRandomNumbers.count != numberOfNumbers {
let myNumber = arc4random_uniform(higher - lower) + lower
if !myRandomNumbers.contains(Int(myNumber)) {
myRandomNumbers.append(Int(myNumber))
}
}
return myRandomNumbers
}
//Label
#IBOutlet weak var lbl: UILabel!
//Button
#IBAction func action(_ sender: AnyObject) {
if (sender.tag == Int(rightAnswerPlacement)) {
print ("RIGHT!")
} else {
print ("WRONG!!!!!!")
}
if (currentQuestion != questions.count) {
newQuestion()
} else {
}
}
//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
}
Any idea that how will be possible to resolve?

First you need to store the array of random questions that you generate (side note you appear to try to generate 10 random numbers but have only 5 questions).
Then instead of using the currentQuestion directly you use that to access the question in the array.
So change it to this:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
questionArray = generateRandomNumber(0, 9, 10)
}
override func viewDidAppear(_ animated: Bool) {
newQuestion()
}
// Quiz//
let questions = ["Q1","Q2","Q3","Q4","Q5"]
let answers = [["A","2","3"],["B","2","3"],["C","2","3"],["D","2","3"],["E","2","3"]]
//Variables
var currentQuestion = 0
var rightAnswerPlacement:UInt32 = 0
var questionArray: [Int] = [] // Just an initial value
//Generate Random Numbers
func generateRandomNumber(_ from:Int, _ to:Int, _ qut:Int?) -> [Int] {
var myRandomNumbers = [Int]() //All our generated numbers
var numberOfNumbers = qut //How many numbers to generate
let lower = UInt32(from) //Generate from this number..
let higher = UInt32(to+1) //To this one
if numberOfNumbers == nil || numberOfNumbers! > (to-from) + 1 {
numberOfNumbers = (to-from) + 1
}
while myRandomNumbers.count != numberOfNumbers {
let myNumber = arc4random_uniform(higher - lower) + lower
if !myRandomNumbers.contains(Int(myNumber)) {
myRandomNumbers.append(Int(myNumber))
}
}
return myRandomNumbers
}
//Label
#IBOutlet weak var lbl: UILabel!
//Button
#IBAction func action(_ sender: AnyObject) {
if (sender.tag == Int(rightAnswerPlacement)) {
print ("RIGHT!")
} else {
print ("WRONG!!!!!!")
}
if (currentQuestion != questions.count) {
newQuestion()
} else {
}
}
//Function that displays new question
func newQuestion() {
lbl.text = questions[questionArray[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[questionArray[currentQuestion]][0], for: .normal)
} else {
button.setTitle(answers[questionArray[currentQuestion]][x], for: .normal)
x = 2
}
}
currentQuestion += 1
}
(I think the code is correct but I'm currently running some tests in Xcode so can't check. If there is a problem leave a comment.)

Related

How to pass data from ViewDidLoad to a function of a button

I have two Random numbers generating in ViewDidLoad. I Want to plus these two numbers and the result should be checked in a button with data of a input field. my problem is that I do not know how to transfer result to the function CheckResultBtn . now it does not recognise result.
here is My codes and I really appreciate any kinds of help
class ViewController: UIViewController {
#IBOutlet weak var lblRandomNumOne: UILabel!
#IBOutlet weak var lblRandomNumTwo: UILabel!
#IBOutlet weak var KidsField: UITextField!
#IBOutlet weak var Showresult: UILabel!
#IBOutlet weak var CheckResult: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let x = Int.random(in: 0 ..< 10)
lblRandomNumOne.text = String(x);
let y = Int.random(in: 0 ..< 10)
lblRandomNumTwo.text = String(y);
let result = x + y;
CheckResultBtn(result) //not sure about this
}
#IBAction func CheckResultBtn(_ sender: Any)
{
if (KidsField != nil)
{
let kidsresult = Int(KidsField.text!) ;
if (result == kidsresult)
{
Showresult.text = "Bravo";
}
else
{
Showresult.text = "Try Again!"
}
}
}
}
declare the variable "result" before the viewDidload function
var result : Int!
then proceed to perform the operation on the variable in your viewDidload and you can get rid of this line
CheckResultBtn(result)
and you should be able to access the result variable in your IBAction function
You can try
#IBAction func checkResultBtn(_ sender: Any) {
displayRes(Int(kidsField.text!)!)
}
func displayRes(_ result:Int) {
showresult.text = result == kidsresult ? "Bravo" : "Try Again!"
}
Then inside viewDidLoad
displayRes(x+y)
create a new function instead of using action Method
like this :
func CheckResultBtn(_ result : Int) {
if (KidsField != nil)
{
let kidsresult = Int(KidsField.text!) ;
if (result == kidsresult)
{
Showresult.text = "Bravo";
}
else
{
Showresult.text = "Try Again!"
}
}
}
or create a variable
var result : Int?
assign a value
result = x + y;
and use inside the action Method
var result : Int?
override func viewDidLoad() {
super.viewDidLoad()
let x = Int.random(in: 0 ..< 10)
lblRandomNumOne.text = String(x);
let y = Int.random(in: 0 ..< 10)
lblRandomNumTwo.text = String(y);
result = x + y;
}
#IBAction func CheckResultBtn(_ sender: Any) {
if (KidsField != nil)
{
let kidsresult = Int(KidsField.text!) ;
if (result == kidsresult)
{
Showresult.text = "Bravo";
}
else
{
Showresult.text = "Try Again!"
}
}
}

How to remove "..." from my array?

In my calculator app I ran into a problem where I want ... to show in my array but only when the if statement for resultIsPending is true. Then after that I want the ... to be deleted. How can I do this in Swift? Here is the code of my ViewController.swift:
#IBOutlet weak var sequence: UILabel!
#IBOutlet weak var display: UILabel!
var userInTheMiddleOfTyping = false
var resultIsPending:Bool = false
var elements = [String]()
//var sequenceArray:Array = []
#IBAction func clear(_ sender: Any) {
display.text = " "
elements.removeAll()
elements = elements.filter{$0 != "\(String(describing: display.text))"}
sequence.text = elements.joined()
}
override func viewDidLoad() {
}
#IBAction func touchDigit(_ sender: UIButton) {
let digit = sender.currentTitle!
elements.append(digit)
combineToMakeOperationHistory()
if userInTheMiddleOfTyping{
let textCurrentlyInDisplay = display!.text!
display!.text = textCurrentlyInDisplay + digit
} else {
display!.text = digit
userInTheMiddleOfTyping = true
}
}
var displayValue: Double{
get{
return Double(display.text!)!
}
set{
display.text = String(newValue)
}
}
private var brain = CalculatorBrain()
#IBAction func performOperation(_ sender: UIButton) {
let perSender = sender.currentTitle!
elements.append(perSender)
combineToMakeOperationHistory()
if perSender == "+" || perSender == "÷" || perSender == "×" || perSender == "-" || perSender == "^"{
resultIsPending = true
}
if userInTheMiddleOfTyping{
brain.setOperand(displayValue)
userInTheMiddleOfTyping = false
}
userInTheMiddleOfTyping = false
if let mathematicalSymbol = sender.currentTitle{
brain.performOperation(mathematicalSymbol)
}
if brain.result != nil{
displayValue = brain.result!
}
}
func combineToMakeOperationHistory() {
if resultIsPending{ // this is the if statement
elements.append("...")
}else if resultIsPending == false{
}
sequence.text = elements.joined()
}
You can filter your elements array and remove the "...".
elements = elements.filter({ $0 != "..." })
Whenever you want to remove the occurrence of a String value.
you can uses something like hat
var resultIsPending:Bool = false{
didSet(isPending) {
if isPending {
elements.append("...")
} else {
elements.dropLast()
}
}
}
Don't combine data that are not of the same type. There is no reason to put ... into the array of elements:
func combineToMakeOperationHistory() {
var sequenceText: String = elements.joined()
if (resultIsPending) {
sequenceText += "..."
}
sequence.text = sequenceText
}
Since we are not appending ... to the array, we don't have to remove it.

Add a history on calculator (iOS)

I'm having a problem adding a history to calculator (lblHistory Label)
I want to store my previous inputs in the lblHistory : UILabel
but all it did is store the first number and its operation.
Is there any algorithm that you will store all previous inputs of your calculator?
This is my class MyCalculator.swift
Class MyCalculator
//
// MyCalculator.swift
// Calculator
//
// Created by
// abcdefgh
//
import Foundation
class MyCalculator
{
var memory: Float = 0
var percentnum: Float = 0
var currentNum: Float = 0
var result: Float = 0
var operation: String = ""
var isOperation: Bool = false
var isRepeatOperation: Bool = false
var isNum: Bool = false
func operation (operation:String) -> Float
{
if operation == "+" {
result = result + currentNum
}
else if operation == "-" {
result = result - currentNum
}
else if operation == "*" {
result = result * currentNum
}
else if operation == "/" {
result = result / currentNum
}
return result
}
func percent(percentNum:Float) ->Float
{
let valpercent = percentNum * 0.01
result = result * valpercent
return result
}
func clearAll() {
result = 0
currentNum = 0
isNum = false
}
}
and this is the view controller of my calculator.
View Controller
/ ViewController.swift
// Calculator
//
// Created by
// abcdefgh
//
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.
}
//Start Calculator
//Label History: This is where the history is displayed
#IBOutlet weak var lblHistory: UILabel!
//Label Result: This is where the result is displayed
#IBOutlet weak var lblResult: UILabel!
//Initialization
let calc = MyCalculator() //call the class MyCalculator
//Function for Number Inputs
#IBAction func btnNumInput(sender: AnyObject)
{
let num = sender.currentTitle
if calc.isNum == false
{
lblResult.text = num
calc.isNum = true
}
else
{
lblResult.text = lblResult.text! + num!!
}
calc.isRepeatOperation = false
lblResult.adjustsFontSizeToFitWidth = true
}
//Function for Operations
#IBAction func btnOperations(sender: AnyObject)
{
calc.isNum = false
if calc.isOperation && !calc.isRepeatOperation
{
calc.currentNum = Float(lblResult.text!)!
if calc.operation == "+"
{
calc.result = calc.operation("+")
}
else if calc.operation == "-"
{
calc.result = calc.operation("-")
}
else if calc.operation == "*"
{
calc.result = calc.operation("*")
}
else if calc.operation == "/"
{
calc.result = calc.operation("/")
}
if calc.operation == "/" && calc.currentNum == 0
{
lblResult.text = "Cannot divide by zero"
lblResult.adjustsFontSizeToFitWidth = true
}
else
{
let stringresult = String(format:"%g" , calc.result)
lblResult.text = "\(stringresult)"
calc.isOperation = false
}
}
else
{
calc.result = Float(lblResult.text!)!
}
let strresult = String(format:"%g", calc.result)
calc.operation = sender.currentTitle!!
lblHistory.text = strresult + calc.operation
calc.isOperation = true
calc.isRepeatOperation = true
}
From this code here:
let strresult = String(format:"%g", calc.result)
calc.operation = sender.currentTitle!!
lblHistory.text = strresult + calc.operation
this should store any input from the history label but it will only store the first input and its operator.
The result:
2+2 =4 //this is the input variables and operation
//the result is 4 and display on lblResult: UILabel
2+ // the "2+" is only stored in lblHistory: UILabel not the whole input itself

Array element checking in swift

Currently implementing a favourite function to my application which is based on quotes. I would like to perform a check for if the user has already saved an element to an array. If they have, I would change a favour button to unfavour button.
import UIKit
class DreamViewController: UIViewController {
#IBOutlet var liketext: UIButton!
#IBOutlet var QuoteImage: UIImageView!
#IBOutlet weak var DreamLabel: UILabel!
var counter = 1
var factIndex = 0
let dreamFact = Dream()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
DreamLabel.text = dreamFact.dreamArray[factIndex]
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func LeftSwipe() {
factIndex--
var number = dreamFact.dreamArray.count - 1
if (factIndex < 0){
self.factIndex = number
}
DreamLabel.text = dreamFact.dreamArray[factIndex]
if counter == 1 {
counter = 36
} else {
self.counter--
}
QuoteImage.image = UIImage(named: "frame\(counter).jpg")
}
#IBAction func RightSwipe() {
factIndex++
if factIndex >= dreamFact.dreamArray.count {
self.factIndex = 0
}
DreamLabel.text = dreamFact.dreamArray[factIndex]
if counter == 36 {
counter = 1
} else {
self.counter++
}
QuoteImage.image = UIImage(named: "frame\(counter).jpg")
}
struct Constants {
static let defaults = NSUserDefaults.standardUserDefaults()
static let fqKey = "FavoriteQuotes"
static let like = "Like"
static let unlike = "Unlike"
}
class DreamViewController {
var favouriteQuotesArray = Constants.defaults.objectForKey(Constants.fqKey) as? [String] ?? [String]() {
didSet {
Constants.defaults.setObject(favouriteQuotesArray, forKey: Constants.fqKey)
}
}
#IBOutlet weak var likeButton: UIButton! {
didSet {
let favourite = dreamArray[factIndex]
if let foundIndex = find(favouriteQuotesArray, favourite) {
likeButton.setTitle(Constants.unlike, forState: .Normal)
}
else {
likeButton.setTitle(Constants.like, forState: .Normal)
}
}
}
#IBAction func likeButtonClicked(sender: UIButton) {
}
}
}
The dependency on upon the text which is unreliable because if the user reopens the app, the element would still be saved instead of starting out with the default unlike. If the element is found in the array. How would I perform a search inside my favouriteQuotesArray if the element exists like:
if elementfound statement {
button.settitle(unlike)
removearrayatindex statement.
} else {
button.settitle(like)
makeQuoteFavourite()
}
How would the array checking statement look like?
P.S If you can please give tips on building the delete class?
Update:
var favouriteQuotesArray: [String] = NSUserDefaults.standardUserDefaults().objectForKey("thekey") as! [String]
func makeQuoteFavourite() {
if favouriteQuotesArray == nil {
favouriteQuotesArray = []
}
let currentQuote = dreamFact.dreamArray[factIndex]
favouriteQuotesArray.append(currentQuote)
NSUserDefaults.standardUserDefaults().setObject(favouriteQuotesArray, forKey: "thekey")
}
#IBAction func likeButton() {
if contains(favouriteQuotesArray, dreamFact.dreamArray[factIndex]) {
liketext.setTitle("Unlike", forState: UIControlState.Normal)
}
Do it this way; some cleaner coding techniques to cut through the rubble included.
The declaration of favouriteQuotesArray is a gold star one liner. Got it all?
import UIKit
struct Constants {
static let defaults = NSUserDefaults.standardUserDefaults()
static let fqKey = "FavoriteQuotes"
static let like = "Like"
static let unlike = "Unlike"
}
class Page2ViewController: UIViewController {
var dreamArray = [String]() // place holder so class compiles
var factIndex = 0 // place holder so class compiles
var favouriteQuotesArray = Constants.defaults.objectForKey(Constants.fqKey) as? [String] ?? [String]() {
didSet {
Constants.defaults.setObject(favouriteQuotesArray, forKey: Constants.fqKey)
}
}
#IBOutlet weak var likeButton: UIButton! {
didSet {
let favourite = dreamArray[factIndex]
if let foundIndex = find(favouriteQuotesArray, favourite) {
likeButton.setTitle(Constants.unlike, forState: .Normal)
}
else {
likeButton.setTitle(Constants.like, forState: .Normal)
}
}
}
#IBAction func likeButtonClicked(sender: UIButton) {
let favourite = dreamArray[factIndex]
if let foundIndex = find(favouriteQuotesArray, favourite) {
sender.setTitle(Constants.like, forState: .Normal)
favouriteQuotesArray.removeAtIndex(foundIndex)
}
else {
sender.setTitle(Constants.unlike, forState: .Normal)
favouriteQuotesArray.append(favourite)
}
}
}
Try this:
let currentQuote = dreamFact.dreamArray[factIndex]
if find(favouriteQuotesArray!, currentQuote) != nil {
//Do something
}
The better and quick way to find out an element in an array is:
if contains(yourFavArray, stringStatement){
//Contains you can write your logic
}
Change your favouriteArray as below:
var favouriteQuotesArray: [String] = NSUserDefaults.standardUserDefaults().objectForKey("thekey") as! [String]
Edited:
if let foundIndex = find(favouriteQuotesArray, dreamFact.dreamArray[factIndex]) {
sender.setTitle(Constants.like, forState: .Normal)
favouriteQuotesArray.removeAtIndex(foundIndex)
}

How to code a calculator that adds, numbers consecutively (Swift)

This is my code for the calculator, right now I can only add 2 numbers consecutively before hitting the equals sign, I want to make where I can add More than two Numbers before hitting the equals button. (Written in Swift)
import UIKit
class CalcControl: UIView {
var firstEntry = ""
var isUserInMiddleOfTypingNumbers:Bool?
var didUserPressedOperation:Bool?
var op:String?
#IBOutlet var displayLabel: UILabel!
#IBAction func digitPressed(sender: UIButton) {
if (didUserPressedOperation == nil) {
//displayLabel.text = ""
didUserPressedOperation = nil
}
if (isUserInMiddleOfTypingNumbers == nil) {
displayLabel.text = displayLabel.text! + sender.currentTitle!
} else {
displayLabel.text = sender.currentTitle
isUserInMiddleOfTypingNumbers = true
}
}
#IBAction func operationPressed(sender: UIButton) {
op = sender.currentTitle
firstEntry = displayLabel.text!
didUserPressedOperation = true
displayLabel.text = ""
}
#IBAction func clearPressed(sender: UIButton) {
self.clear()
}
func clear() {
firstEntry = ""
isUserInMiddleOfTypingNumbers = nil
didUserPressedOperation = nil
displayLabel.text = ""
}
#IBAction func equalPressed(sender: UIButton) {
var secondEntry:NSString = displayLabel.text!
println((firstEntry))
println((secondEntry))
if(op == "+") {
displayLabel.text = (((firstEntry as NSString!).doubleValue + (secondEntry as NSString).doubleValue) as NSNumber).stringValue
} else if(op == "-") {
displayLabel.text = (((firstEntry as NSString!).doubleValue - (secondEntry as NSString).doubleValue) as NSNumber).stringValue
} else if(op == "x") {
displayLabel.text = (((firstEntry as NSString!).doubleValue * (secondEntry as NSString).doubleValue) as NSNumber).stringValue
} else if(op == "/") {
displayLabel.text = (((firstEntry as NSString!).doubleValue / (secondEntry as NSString).doubleValue) as NSNumber).stringValue
}
firstEntry = ""
isUserInMiddleOfTypingNumbers = nil
didUserPressedOperation = nil
}
}
I would adjust your logic so that:
There is an IB Action that all the operator keys (+, -, x, /) are tied to. In this IB Action any time it is activated, there are two choices: a) prepare for another number to be entered or b) the previous key sequence was a number followed by an operator symbol and the button is the same as an Enter.
An IB Action for the delete key. Obvious role.
An IB Action for the Enter key.

Resources