Ep.19[ wen’s iOS ] — 世界之最選擇題App

本篇練習 array、struct 自訂資料型別 、使用 Codable csv 匯入資料

做這個作業前一直在想到底要以什麼為題目,想到一直以來都對世界之最蠻有興趣的( 冷知識的概念 ),但請 chatgpt 出以地理世界之最為題目發現錯誤率太高了,很多正確答案都不正確,不知道是不是 3.5版本的關係,最後還是用維基百科查資料一項一項去 key 進表格中。

App 實作

storyboard 建立

layout

如上圖,這次用到的新元件有 progress view 、還有 [ UIButton ] 做出選擇題選項的 Action 觸發。

程式部分

使用 array 、 struct 定義型別 ( Question ) 、使用 .shuffle 重新排序 array 、使用 Codable csv 匯入資料等。

1. struct 建立資料格式、匯入資料庫

建立一個 swift file 資料夾,定義 struct 。(每定義一個 struct 都須建立一個新資料夾)

// 定義一個適用於讀取 CSV 資料的結構
struct Question: Codable {

let questionText: String // 題目的文字
let options: String // 選項的文字
let correctAnswerText: String // 正確答案
}

參考以下文章建立好表格匯出 .csv 檔案並匯入 Xcode — Asset

問題、選項與正確答案
// 在 Question.swift 中新增了一個擴展
extension Question {
static var data: [Self] {
var array = [Self]()

// 檢查是否能夠讀取名為 "Geography" 的資料檔案
if let data = NSDataAsset(name: "Geography")?.data {

// 使用 CSVDecoder 來解碼 CSV 資料
let decoder = CSVDecoder {
$0.headerStrategy = .firstLine
}
do {
// 將資料解碼為 Question 的陣列
array = try decoder.decode([Self].self, from: data)
} catch {
print(error)
}
}
// 返回解碼後的陣列
return array
}
}

NSDataAsset(name: "")? 中改為 .csv 的檔名,在 view controller 中加上 let array名稱 = struct名稱.data ,.csv 檔案就整份被 swift 讀取了,後面就可以正常用 array 的方式取用。

class ViewController: UIViewController {
// 儲存從 Question.data 中取得的問題,這是一個由 Question 結構所組成
var question = Question.data
// 用於儲存目前要顯示的問題,初始值與 questions 相同
var currentQuestion = Question.data

2. 設置問題

// 設置問題內容和選項按鈕的方法
func setQuestion() {
questionContentLabel.text = currentQuestion[index].questionText

// 將選項拆分為陣列並設置選項按鈕的標題
let optionsArray = currentQuestion[index].options.split(separator: ",").map(String.init)

for i in 0..<optionsArray.count {
optionButtons[i].setTitle(optionsArray[i], for: .normal)

// 啟用所有選項按鈕
for button in optionButtons {
button.isEnabled = true
}
}
}

首先,從 currentQuestion 中取出特定索引 index 的問題,然後取得該問題的選項字串。使用 split(separator: ",") 方法,將選項字串以逗號為分隔符拆分為多個子字串,並返回一個 Substring 陣列。

接著,使用 map(String.init)Substring 陣列轉換為 String 陣列。這將選項字串的每個子字串轉換為獨立的 String 元素。

最後,使用迴圈 for i in 0..<optionsArray.count,遍歷 optionsArray 陣列中的每個元素。在每次迴圈中,將選項陣列中的元素設置為對應的按鈕標題,使用 optionButtons[i].setTitle(optionsArray[i], for: .normal)

3.進度條設置

// 更新進度條和標籤的方法
func progressViewKit() {
testProgressView.progress = Float(index + 1) / 10
progressViewLabel.text = "第 \(index + 1) 題"
}

Progress View 的 value 位於 0~1 之間,型別為 Float。故我們需要將 index ( Int ) 先轉為 Float 並除以 10,以在 progress view 上顯示。

4.重新開始

// 按下重新開始遊戲按鈕時的動作
@IBAction func restartGameButton(_ sender: UIButton) {
// 打亂問題順序並取出前 10 題
question.shuffle()
currentQuestion = Array(question.prefix(10))
index = 0
setQuestion()
testProgressView.progress = 0.1
progressViewLabel.text = "第 1 題"
score = 0
answerResultLabel.isHidden = true
questionContentLabel.isHidden = false
scoreLabel.isHidden = true
restartButton.isHidden = true
nextButton.isHidden = true
}

所有設置都歸 0 ,所有 label 設隱藏,進度條設為 0.1 ,計分歸 0 ,以及最重要的 shuffle question Array 並 prefix 前 10 個當題目。

5.下一題

// 按下下一題按鈕時的動作
@IBAction func nextButton(_ sender: Any) {
if index < currentQuestion.count - 1 {
index += 1
setQuestion()

questionContentLabel.isHidden = false
answerResultLabel.isHidden = true
nextButton.isHidden = true
correctAnwserLabel.isHidden = true
} else {
answerResultLabel.isHidden = true
nextButton.isHidden = true
restartButton.isHidden = false
correctAnwserLabel.isHidden = true
getScoreResult()
}

progressViewKit()
}

分為第十題以前顯示答案與第十題之後要顯示分數與再玩一次。

感想

這次修改了很多 button 跟 label 的互動,確保下一題與再玩一次時這些元件不會出現在不該出現的時候出現,以及該出現的時候沒出現 ; 本次作業大重點 .csv檔案這次老實說還沒有很熟稔,部分是還有參考同學的解說,之後再找別的作業練習。以及另一個大重點 struct ,本是是第一次實作,詳細使用時機還要再多練習。

--

--