How to concatenate arrays when button is selected - ios

So I'm working on a quiz-app and I want to give the user an option screen before starting the quiz, where the user can choose which categories the quiz should have.
Its currently only working with 1 category at a time but I want to be able to concatenate the questionArrays when the user selects button in the option screen.
At this time the user selects a button and depending on the sender.tag an Int gets passed through the var called 'pickedCategory' which then decides which of the arrays to base the questions on. This happens in the prepare for segue:
let selectedCategory = quizCategories[pickedCategory]
secondVC.allQuestions = selectedCategory.questions
quizCategories is declared at the top:
var quizCategories = [QuestionArray]()
What I would like to do is have 4 buttons and whenever one is selected, concatenate an array to the selectedCategory
For instance, button 1 & 2 is selected, i.e. the user wants to have questions based on 2 categories. Resulting in something like this?
let selectedCategory = quizCategories[0].questions + quizCategories[1].questions
and if 3 buttons selected, add another quizCategories array to the final array etc etc
Please note, its not HOW to concatenate I'm looking for, its how to automatically do this depending on buttons selected..
This is my first question posted and I am very new to Swift.. hope I didn't confuse u guys.. thanks :)

What I would suggest is for each category button, keep track of which is selected (and even deselected if the user decides they don't want that category), by adding or removing from an array of categoryIDs. Then, once the "Done" button (or whatever final decision button is tapped), take the array of categoryIDs they selected, and flatMap over them to produce the ultimate array of questions.
let questions = categoryIDs.flatMap { quizCategories[$0].questions }
Now, you'd have your array of questions to present to the user. Use flatMap vs map, because you'd want to flatten the returned array of arrays into a single array.

Tags are not the best way to identify buttons, but I'll ignore that.
Don't use buttons, use UISwitches. Have a switch for each category.
Say you have a constant switchTagBase, with a value of 100:
let switchTagBase = 100
So the switch for your first category has a tag of 100, the next one has a tag of 101, etc.
You already have an array of categories, which is good.
Now, when it's time to build your array of selected questions, simply go through the categories, figure out which switches are on, and add in those categories
var selectedCategories = [QuestionArray]()
for (index, array) in quizCategories.enumerated() {
let thisTag = index + switchTagBase
guard let thisSwitch = view.viewWithTag(thisTag),
thisSwitch.isOn else { continue }
selectedCategories += quizCategories[index]
}
(Note that you should really maintain an array of selection states based on the switches the user activates, and iterate through that array rather than looping through the switches directly, but in the interest of brevity I fetched switch states directly.)

You can track which button is selected and when you want to start quizz, concatenate the corresponding arrays.
Your code could look like this:
import UIKit
class QuizzSelection: UIViewController
{
#IBAction func firstButtonAction(_ sender: Any)
{
firstButtonWasClicked()
}
#IBAction func secondButtonAction(_ sender: Any)
{
secondButtonWasClicked()
}
#IBAction func goForwardWithQuizz(_ sender: Any)
{
startQuizzWasClicked()
}
var isFirstCategorySelected = false
var isSecondCategorySelected = false
let firstCategoryQuestions = ["Question Cat 1"]
let secondCategoryQuestions = ["Question Cat 2"]
var emptyArrayOfQuestions = [String]()
func firstButtonWasClicked()
{
if isFirstCategorySelected == true{
isFirstCategorySelected = false
} else{
isFirstCategorySelected = true
}
}
func secondButtonWasClicked()
{
if isSecondCategorySelected == true{
isSecondCategorySelected = false
} else{
isSecondCategorySelected = true
}
}
func startQuizzWasClicked()
{
if isFirstCategorySelected == true{
emptyArrayOfQuestions += firstCategoryQuestions
}
if isSecondCategorySelected == true{
emptyArrayOfQuestions += secondCategoryQuestions
}
}
}
EDIT
Improved code for six categories:
import UIKit
class QuizzSelection: UIViewController
{
#IBAction func firstButtonAction(_ sender: Any)
{
firstButtonWasClicked()
}
#IBAction func secondButtonAction(_ sender: Any)
{
secondButtonWasClicked()
}
// Four mour button actions
#IBAction func goForwardWithQuizz(_ sender: Any)
{
startQuizzWasClicked()
}
var wichCategoryAreSelected = [false, false, false, false, false, false] //six categories
var arrayOfQuestions = [["Question 1 Cat 1","Question 2 Cat 1"], ["Question 1 Cat 2", "Question 2 Cat 2"], ...]
var emptyArrayOfQuestions = [String]()
func firstButtonWasClicked()
{
wichCategoryAreSelected[0] = !wichCategoryAreSelected[0]
}
func secondButtonWasClicked()
{
wichCategoryAreSelected[1] = !wichCategoryAreSelected[1]
}
func startQuizzWasClicked()
{
for i in 0...(wichCategoryAreSelected.count-1)
{
if wichCategoryAreSelected[i]
{
emptyArrayOfQuestions += arrayOfQuestions[i]
}
}
}
}

Related

Displaying and Playing MPMediaItem songs in retrieved + last played order

I'm building an app that makes use of MPMusicPlayerApplicationController to play music from a user's local music library.
The songs from the library are retrieved like so:
func retrieveMusic() {
mediaQueryQueue.async {
let songsQuery = MPMediaQuery.songs()
let songsCollections = songsQuery.collections
for songCollection in songsCollections ?? [] {
let songs = songCollection.items
self.retrievedSongs.append(contentsOf: songs)
}
DispatchQueue.main.async {
self.setLoadedSongsUIState()
self.setupTableView()
self.sortOrderDidChange()
}
}
}
These songs are then displayed in a table view:
func setTableViewItems(_ songs: [MPMediaItem]) {
let section = self.tableViewAdaptor!.sections[0] as! TableViewAdaptorSection<SongTableViewCell, MPMediaItem>
section.items = songs
tableViewAdaptor.tableView.reloadData()
}
I also have a segment control that will determine whether to display the songs in the order in which they were retrieved, or in the last played order; and then update the table view based on which choice was selected:
var retrievedSongs: [MPMediaItem] = []
var sortedSongs: [MPMediaItem] = []
private func sortOrderDidChange() {
switch sortOrder {
case .playDateDescending:
let unixDate = Date(timeIntervalSince1970: 1000)
sortedSongs = retrievedSongs.sorted {
$0.lastPlayedDate ?? unixDate > $1.lastPlayedDate ?? unixDate
}
default:
sortedSongs = retrievedSongs
}
setTableViewItems(sortedSongs)
let queueDescriptor = MPMusicPlayerMediaItemQueueDescriptor(itemCollection: MPMediaItemCollection(items: sortedSongs))
queueDescriptor.startItem = musicPlayer.nowPlayingItem
musicPlayer.setQueue(with: queueDescriptor)
selectPlayingItem(scrollToVisible: true)
}
The user is able to use buttons to play/pause, and go to the next or previous tracks:
#IBAction func forwardButtonPressed(_ sender: Any) {
musicPlayer.skipToNextItem()
selectPlayingItem(scrollToVisible: true)
}
#IBAction func backButtonPressed(_ sender: Any) {
musicPlayer.skipToPreviousItem()
selectPlayingItem(scrollToVisible: true)
}
Additionally, if you select a track in the table view, that track will be played:
tableViewAdaptor = TableViewAdaptor(
tableView: songTableView,
sections: [songsLibrarySection],
didChangeHandler: {
let selectedSongIndex = self.songTableView.indexPathForSelectedRow
if selectedSongIndex != nil {
let selectedSong = self.retrievedSongs[selectedSongIndex!.row]
self.musicPlayer.nowPlayingItem = selectedSong
self.musicPlayer.play()
self.updatePlaybackUI(scrollNowPlayingToVisible: true)
}
})
setTableViewItems(retrievedSongs)
When the songs are displayed in the order in which they were retrieved, everything works perfectly. If I click back or forwards, or select a song to play; the correct song will play in the next order.
Right now if you're in the "order retrieved" tab, and you go to the "ordered by date" tab and click backwards or forward, the next song will not be whatever is next that's displayed in the table view, but in the "retrieved" by order.
What I'm trying to do is change the code so that no matter whether the retrieved or ordered songs are displayed, the next or previous song will correspond to what's displayed.

Not Repeating Word in Swift when clicking button - IOS

How can I not repeat the array when I click on the button in swift? I'm trying to generate fruits without them repeating.
import UIKit
class fruitrandomViewController: UIViewController {
#IBOutlet weak var nextfruitButton: UIButton!
#IBOutlet weak var fruitbox: UILabel!
#IBAction func fruitbutton(_ sender: UIButton) {
let array = ["Apple","Banana","Orange","Pinapple", "Plum", "Pear",]
let randomFruitgenerator = Int(arc4random_uniform(UInt32(array.count)))
fruitbox.text = array[randomFruitgenerator]
}
}
My suggestion is to use a Set and remove the random item from the set
var set = Set(["Apple","Banana","Orange","Pinapple", "Plum", "Pear"])
#IBAction func fruitbutton(_ sender: UIButton) {
if let fruit = set.randomElement() {
fruitbox.text = fruit
set.remove(fruit)
} else {
fruitbox.text = "" // or whatever to indicate that the set is empty
}
}
My suggest:
You can random each item 1 time
let originFruits = ["Apple","Banana","Orange","Pinapple", "Plum", "Pear"]
let array = originFruits
#IBAction func fruitbutton(_ sender: UIButton) {
...
let fruitRandom = array random
array delete fruitRandom
if (array empty) {
array = originFruits
}
}
You can check and remove in next time
let originFruits = ["Apple","Banana","Orange","Pinapple", "Plum", "Pear"]
let array = originFruits
let skipFruit = ""
#IBAction func fruitbutton(_ sender: UIButton) {
...
array = originFruits
array delete skipFruit
let fruitRandom = array random
skipFruit = fruitRandom
}
I think this will work, but I think it's Time Complexity can be O(n) as I am assuming that there is a possibility of randomElement return Apple every time and savedFruit is also Apple, so in that case, the Time Complexity will be O(n). A better workaround will be to remove that element from that array so, the next time the randomElement will be different for sure. Then, once it is different you can append the old one and remove the current one. I hope it makes sense to you, but for now this will work I think:
let array = ["Apple","Banana","Orange","Pinapple", "Plum", "Pear"]
var savedFruit = String()
func fetchRandomFruit() {
if let fruit = array.randomElement() {
if fruit != savedFruit { //Here it will check if the last element is same as the new randomElement
savedFruit = fruit
fruitbox.text = savedFruit
} else {
fetchRandomFruit()
}
}
}
#IBAction func fruitbutton(_ sender: UIButton) {
fetchRandomFruit()
}
Use fruitbox.text = array.randomElement() ?? "default value for if array is empty". This is not guaranteed not to repeat, but is random.
If you want to ensure it does not repeat, use this
var new = array.randomElement() ?? fruitbox.text
while new == fruitbox.text {
new = array.randomElement() ?? fruitbox.text
}
fruitbox.text = new
This WILL loop infinitely (until it crashes) if the array is empty.

Save data randomly generated into label

I am fairly new to coding and was wondering if it was possible to save data I have randomly generated into a label. I can randomly generate the data and input it into the label but I am having difficulties saving the previous data from the label to an array. Is it possible to do this? And how would it be done?
Essentially I want the user to be able to go back to the previous text in the label if they press a button
Thank you for any help.
code:
This is how I am generating the random variable from an array which contains quotes
//right hand button tapped
#IBAction func RHSButton(_ sender: Any) {
// randomises variable to display
var randomQuote: String {
let randomNumber = Int(arc4random_uniform(UInt32(myQuotes.count)))
return myQuotes[randomNumber]}
quotesLabel.text = randomQuote
}
I want to be able to store the data into an array called:
var randomGeneratedQuotes : [String] = []
But I am not quite sure how
hi seems like u want undo feature.
take one more array and add selected randomNumber
on clicking button display last index of array and remove it from array.
If I understand you correctly you could save the value of your label into your randomGeneratedQuotes array just before you generate a new random value.
var randomQuote: String {
let randomNumber = Int(arc4random_uniform(UInt32(myQuotes.count)))
return myQuotes[randomNumber]
}
#IBAction func RHSButton(_ sender: Any) {
//If a value is present, save it
if let oldValue = quotesLabel.text {
randomGeneratedQuotes.append(oldValue)
}
//and update the value
quotesLabel.text = randomQuote
}
From your previous questions I'm guessing you'd like a LHSButton function to give you the previous value, that could look like so:
#IBAction func LHSButton(_ sender: Any) {
//Do we have a previous value?
if randomGeneratedQuotes.count > 0 {
//take that
let previousValue = randomGeneratedQuotes.removeLast()
//and use it
quotesLabel.text = previousValue
}
}
Hope that works and helps.

switch of multiple labels from two different views

I implemented a button action that switch from one view with a UIlabel.text to another view that will display another specifical label that is corresponding .
The method i am using, at present is as follow:
func switchCard(_ sender: Any) {
if item1.text == "Fabulae"{
item2.text = " expriment"
}
if item1.text == " simulacra"{
item2.text = "finxere"
}
if item1.text == "tergentes"{
item2.text = "icet"
} }
And its working , but I need to repeat so many times a hundred time for each item so i am wondering if it does exist a simpler way
I was thinking to build to variables with my labels for item 1 and Item2 so they will share the same index and implement a single statement that will make swift able to pick up the right content when i am cliking on the button.
I am going in the correct way ?
Thank you in advance to read me and your kind help .
Regards,
Try this:
enum TypeName: String {
case fabulae = "Fabulae"
case simulacra = " simulacra"
case tergentes = "tergentes"
func getNeededText() -> String {
switch self {
case .fabulae: return " expriment"
case .simulacra: return "finxere"
case .tergentes: return "icet"
}
}
}
func switchCard(_ sender: Any) {
item2.text = TypeName(rawValue: item1.text ?? "")?.getNeededText()
}
OR you can use a dictionary:
let pairs = ["Fabulae": " expriment", " simulacra": "finxere", "tergentes": "icet"]
func switchCard(_ sender: Any) {
item2.text = pairs[item1.text]
}

How can I check if the text of a button contains an element of an array?

I'm pretty new to this so bear with me. What I'm trying to do is check if the button is currently displaying an element from an array; array Answers. What would be the best route to do so?
#IBAction func qButton1(_ sender: Any) {
if (sender as AnyObject).currentTitle == (Answer in Answers){
PickQuestion()
}
else{
missedWords.append(quizLabel.text!)
}
}
Not sure why you pass Any as sender and then cast to AnyObject. Anyway I would use filter in your case:
Suppose you have an array of Answer objects called answers and Answer has a property title
if let _ = answers.filter( { $0.title == (sender as AnyObject).currentTitle) }).first {
PickQuestion()
} else{
missedWords.append(quizLabel.text!)
}
You should not use the button's title to store data and compare it to a value.
Any time you write code who's logic depends on text displayed to the user you are paining yourself into a corner in terms of localization.
Using a numeric tag to index into an array of strings would be a much better idea.
Say you create an array of title strings.
Make each button's tag be 100 + the array index.
let startingTag = 100
#IBAction func qButton1(_ sender: UIButton) {
guard let index = sender.tag where startingTag >= 100 else {
return
}
//fetch your string from the array of button title strings you set up.
let stringToFind = strings[index]
if answers.contains(where: {$0 == stringToFind}) {
//Found the current' buttons' string in answers
} else {
//string not found
}
}

Resources