How do I access this array of structs - Swift - arrays

I have created an array called "questions" that is full of instances of the struct "QuizQuestion" - Basically I want the "nextQuestion" function to pull the next question from the array of structs (questions) and display it to the user (I understand how to present the data through labels etc, I just don`t know how to access it).
I have tried calling the array index and then the instance but that doesnt seem to work. I have also tried creating an instance of "QuizQuestion" as a variable and using that in the "NextQuestion" function which works but I don`t know how to automatically draw questions from "questions".
Thanks
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var questionLabel: UILabel!
#IBOutlet weak var answerLabel: UILabel!
#IBOutlet weak var explanationLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
questionVariable()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
struct QuizQuestion {
let question: String
let answer: Bool
let explanation: String
}
let questions: [QuizQuestion] = [
QuizQuestion(question: "Red is a four letter word", answer: false, explanation: "Red is not a four letter word"),
QuizQuestion(question: "Dog is a three letter word", answer: true, explanation: "Dog is most likely a three letter word"),
QuizQuestion(question: "Cow is a three letter word", answer: true, explanation: "Cow is most likely a three letter word")
]
var nextQuestion = QuizQuestion.self[1]
func questionVariable() {
if nextQuestion.answer == true {
questionLabel.text = nextQuestion.question
explanationLabel.text = nextQuestion.explanation
answerLabel.text = "Correct"
}
}
}

You can define a nested function (the simplest form of a closure in Swift) as below:
func getNextQuestion() -> (Int) -> QuizQuestion!
{
func incrementor(var index:Int) -> QuizQuestion! {
if index < questions.count && index >= 0 {
return questions[index]
}
else {
return nil
}
}
return incrementor
}
Then you can access your question list as below
let nextQuestion = getNextQuestion()
var question_1 = nextQuestion(0)
var question_2 = nextQuestion(1)
var question_3 = nextQuestion(2)
println("1.question text: \(question_1.question)")
println("2.question text: \(question_2.question)")
println("3.question text: \(question_3.question)")

A simple function to retrieve the question from the array. Declare a instance variable to hold the current index.
var currentIndex = 0
func nextQuestion() -> QuizQuestion {
// initialize current question
var currentQuestion: QuizQuestion = QuizQuestion(question: "", answer: false, explanation: "")
if currentIndex < questions.count {
currentQuestion = questions[currentIndex]
}
currentIndex++
return currentQuestion
}

Related

How to make sure array data isn't reset in a ios quiz app during segue?

Im creating a quiz app to where once you answer a question correctly it segues to another view controller where there is a label that says "correct answer!" and a contains a button that label reads "Continue?". One you press the button it sends you back to the original view controller where the questions are asked. How ever once you segue from the viewcontroller with the "Continue?" button back to the view controller that asks the questions the data is reset and the user is asked questions they have already answered. How could I make it to where the "continue?" button view controller keeps track of the completed questions so they are not asked again in the question view controller? I am new to swift so would I use a delegate or protocol? Or are those things the same? Ive correctly made it segue from the question view controller to the "continue" view controller however I want the data to stay updated once I segue back to the question view controller so no questions that have already been answered are asked again.
Here is my code from the question view controller:
Import UIKit
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)
questionTimer.text = String(counter)
}
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: "wrongViewSegue", sender: self)
}
//proceed screen
func continueSeg() {
performSegue(withIdentifier: "continueSeg", 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))
{
counter = 15
questionTimer.text = String(counter)
print ("Correct!")
continueSeg()
}
else if (sender.tag != Int(rightAnswerBox)) {
wrongSeg()
print ("Wrong!")
timer.invalidate()
questionList = []
}
randomQuestion()
}
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 is my code for the "continue" view controller (all it contains right now is a segue back to the question view controller but I want it to keep track of which questions have already been answered by storing the data from the array of questions in the question view controller):
import UIKit
class ContinueView: UIViewController {
//correct answer label
#IBOutlet weak var correctLbl: UILabel!
//background photo
#IBOutlet weak var backgroundImage: UIImageView!
func backToQuiz() {
performSegue(withIdentifier: "continueSeg", sender: self)
}
#IBAction func `continue`(_ sender: Any) {
backToQuiz()
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
Move your call to randomQuestion from viewDidAppear to viewDidLoad. The viewDidLoad only called once, but viewDidAppear will always be called every time the controller is shown.
Also take note that Apple says
If a view controller is presented by a view controller inside of a popover, this method is not invoked on the presenting view controller after the presented controller is dismissed.
Take a look on discussion about the differences

Swift: Can't insert NSObject into Array as it wants a [String] instead

I have a model object called Hashtag. This simply contains a optional String variable called hashtagName. I fetch the data from my Firebase Database and append the hashtags to my fashionHashtags, which is a [Hashtag]. The issue I have is that I want to append that to my other categoriesArray by using the insertElementAtIndexPath function. I cannot do this as it wants an array of Strings and not an array of Hashtag. When I autocorrect it, it replaces it with fashionHashtags as! [String] but that creates another error. How do I fix this so it allows me to do so? I would like to stick to the Model Object way of doing things. Thank you guys. An answer would be highly appreciated. Code is below:
class Hashtag: NSObject {
vvar hashtagName: String?
}
private let reuseIdentifier = "Cell"
class HashtagView: UICollectionViewController, UICollectionViewDelegateFlowLayout {
var catagoriesArray: [String] = ["FASHION", "FOOD", "HOBBIES", "MUSIC"]
var fashionHashtags = [Hashtag]()
var foodHashtags = [Hashtag]()
var hobbiesHashtags = [Hashtag]()
var musicHashtags = [Hashtag]()
var hashtagsArray: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
self.hashtagsArray.removeAll()
self.navigationController?.navigationBar.tintColor = .white
navigationItem.title = "Hashtag"
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Finished", style: .plain, target: self, action: #selector(finishSelectingHashtags))
self.collectionView?.backgroundColor = .white
self.collectionView?.register(HashtagCell.self, forCellWithReuseIdentifier: reuseIdentifier)
self.collectionView?.contentInset = UIEdgeInsetsMake(10, 0, 0, 0)
handleFetchFashionHashtags()
}
func insertElementAtIndexPath(element: [String], index: Int) {
catagoriesArray.insert(contentsOf: element, at: index)
}
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if indexPath.item == 0 {
insertElementAtIndexPath(element: fashionHashtags, index: indexPath.item + 1)
self.collectionView?.performBatchUpdates(
{
self.collectionView?.reloadSections(NSIndexSet(index: 0) as IndexSet)
}, completion: { (finished:Bool) -> Void in
})
}
}
Based upon my understanding, there could be a couple of different approaches. Here would be the approach of looping through the array of Hashtag objects and appending the hashtagName string property to the categoriesArray of strings.
for hashTagItem in fashionHashtags {
if let hashTag = hashTagItem.hashtagName {
// Appends to categoriesArray as a string
categoriesArray.append(hashTag)
}
}
Another approach would be to build a set of strings and then insert it as it makes sense.
var hashTagString: [Strings] = []
for hashTagItem in fashionHashtags {
if let hashTag = hashTagItem.hashtagName {
hashTagStrings.append(hashTag)
}
}
// Insert or add the hash tag strings as it makes sense
categoriesArray += hashTagStrings // Add all at once if it makes sense

Type '___' has no member 'array'

I am working on this quote app, and I keep running into two errors that just don't want to cooperate with me. It says "Type 'businessQuote' has no member ('array'/'dict')". In the following screen shot, you will see the error on the line. The whole point is to get the app to show a random quote in the text fields provided. Could you please help me? Thank you in advance.
Code with the error
My goal is to get "ImportList" to work
'ImportList' Swift file
If there is another question like this that I have overlooked, I would appreciate it if you could link me to it. But I just really need an answer. Thank you again.
Here's the code with the error:
import Foundation
import UIKit
import Social
class businessQuote: UIViewController {
//============================//
//********** Outlets *********//
//============================//
let utility = Utility()
#IBOutlet weak var quoteDisplay: UILabel!
#IBOutlet weak var authorDisplay: UILabel!
#IBOutlet weak var quoteBackground: UIImageView! //GET BACK TO THIS
//============================//
//********** General *********//
//============================//
let date = NSDate()
var Author: String = ""
var Quote: String = ""
override func viewDidLoad() {
super.viewDidLoad()
// Checks if time is greater then 3pm to change background
let currentTime = utility.currentTime()
if (currentTime >= 15 ) {
quoteBackground.image = UIImage(named: "quote_background.png")
} else {
quoteBackground.image = UIImage(named:"morning_quote_background.png")
}
}
//============================//
//********* New Quote ********//
//============================//
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
// Generates Random Number
func randomNumber(arrayLength: Int) -> Int {
let unsignedArrayCount = UInt32(arrayLength)
let unsignedRandomNumber = arc4random_uniform(unsignedArrayCount)
let randomNumber = Int(unsignedRandomNumber)
return randomNumber
}
// Importing Quotes plist File
let businessQuotes = ImportList(FileName: "BusinessList")
// Selects Quote
let chosenQuote: String = businessQuote.array[randomNumber(businessQuote
.count())] as! String
let chosenAuthor = businessQuote.dict[chosenQuote]! as String
// Assigns Quote & Author to IBOutlet
Author = chosenAuthor
Quote = chosenQuote
quoteDisplay.text = Quote
authorDisplay.text = Author.uppercaseString
}
}
This is the code with the 'array' and 'dict'
import Foundation
struct ImportList {
let path: String
init(FileName: String) {
self.path = NSBundle.mainBundle().pathForResource("\(FileName)", ofType: "plist")!
}
var dict: Dictionary<String, String> {
return NSDictionary(contentsOfFile: path)! as! Dictionary
}
var array: Array<AnyObject> {
return [String](arrayLiteral: String(dict.keys) { $0 as AnyObject as! String })
}
func count() -> Int {
return array.count
}
}
Thank you!
You have declared variable businessQuotes as:
// Importing Quotes plist File
let businessQuotes = ImportList(FileName: "BusinessList")
But using businessQuote instead, see you are missing "s" at the end. Spelling mistake. Following lines should be:
// Selects Quote
let chosenQuote: String = businessQuotes.array[randomNumber(businessQuotes
.count())] as! String
let chosenAuthor = businessQuotes.dict[chosenQuote]! as String

unable to random pick a element for the array Swift

#IBOutlet weak var enterName: UITextField!
#IBOutlet weak var presentName: UITextView!
#IBOutlet weak var independceStatue: UITextField!
#IBOutlet weak var driverSelection: UITextField!
var entity : [Entity] = []
var nameList : [String] = []
var period1 = ""
var counting = 0
override func viewDidLoad() {
super.viewDidLoad()
let context = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
let request = NSFetchRequest(entityName: "Entity")
var results : [AnyObject]?
do{
results = try context.executeFetchRequest(request)
} catch {
results = nil
}
if results != nil {
self.entity = results as! [Entity]
}
if !entity.isEmpty{
presentName.text = entity.last?.period1Core
}
}
func setValues() {
nameList = [enterName.text!]
}
#IBAction func setName(sender: UIButton) {
setValues()
for item in nameList{
period1 += (item + " ")
}
presentName.text = period1
enterName.text = ""
}
#IBAction func start(sender: UIButton) {
let context = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
let entity = NSEntityDescription.insertNewObjectForEntityForName("Entity", inManagedObjectContext: context) as! Entity
entity.period1Core = presentName.text
do {
try context.save()
print("Item Saved")
} catch _ {
print("Saved Failed")
}
**Problem ->** let randomIndex = Int(arc4random_uniform(UInt32(nameList.count)))
driverSelection.text! = nameList[randomIndex]
print(randomIndex)
}
I was trying to randomly pick an element out from the array name nameList, but when I run the program and print the randomIndex, after first time I press the button it will always return last element in the array.
If I exit the simulator and run it again, when I press the button it will return me fatal error Array Index is out of range. Is there somethings wrong with my code, why am I not able to make it randomly select an element from my array?
Your setValues() function always replaces the entire array with the last name entered. That's why it seems to return the last value.
you probably meant to use nameList.append(enterName.text!) in it.
The crash upon starting may occur because nameList has not yet received a name and arc4random_uniform is being called with a parameter value of zero

Setting UILabel Text to a Variable Not Outputting Correctly

I am trying to create a quick test system to make an array and display it. Text is inputted into the text field, and when the button is pressed, the text is added to the array, and the output text is updated. However, if I want to add the data "Sandwich" to the array, it outputs in the label as 'Optional("Sandwich")'. I have already tried using the ? and ! to get it out, but nothing seems to work. Would anyone know how to fix this?
#IBOutlet weak var dataEntryTextField: UITextField!
#IBOutlet weak var addDataButton: UIButton!
#IBOutlet weak var addDataLabel: UILabel!
#IBOutlet weak var dataOutput: UILabel!
var List = [""]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.dataEntryTextField.text = ""
self.dataOutput.text = ""
dataOutput.sizeToFit()
//Add more attributes here
//Apply to the label
dataOutput.backgroundColor = UIColor.grayColor()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func textFieldShouldReturn(TextField: UITextField) -> Bool
{
dataEntryTextField.resignFirstResponder()
return true;
}
#IBAction func addData(sender: UIButton) {
if self.dataEntryTextField.text == "" {}
else {
if List == [""] {List[0] = "• \(self.dataEntryTextField.text)"}
else {List.append("• \(self.dataEntryTextField.text)")}
self.dataOutput.text = List.joinWithSeparator(" ")
}
self.dataEntryTextField.text = ""
dataEntryTextField.resignFirstResponder()
}
You will have to use optional binding to unwrap your optional value. Assume you have an optional text,
var text: String? = "Sandwich"
print("\(text)") // This would print "Optional("Sandwich")"
When its unwrapped using optional binding, you will get the actual text provided the value is not nil.
if let unwrappedText = text {
print("\(unwrappedText)") // This will print "Sandwich"
}
Refer to this link to understand Optionals.
In your code, you will have to unwrap your self.dataEntryTextField.text value and append it to list.

Resources