Swift UIKit iOS App 入門測驗 1

不查資料地從無到有製作選擇題 App,包含以下功能。

  • 製作選擇題 App,每題有四個選項。

ps: 也可以請 AI 出題。

  • 答對一題加 10 分 。
  • 畫面上顯示目前題目是第幾題。
  • 自訂選擇題的資料型別。
  • 題庫有 n 題,隨機出其中的 10 題。( n > 10 )
  • 每次玩的時候題目順序都不一樣。(ps: 進階版: 每次玩的時候題目順序跟選項順序都不一樣)
  • 包含多個頁面,至少有問題頁面和分數頁面。
  • 利用 IBSegueAction & performSegue 將結果從問題頁傳到分數頁。
  • 用 UIAlertController 顯示答對或答錯,答錯時在 alert 裡顯示正確答案。

技術考核項目

  • storyboard。
  • Swift 基本語法。
  • MVC 架構,自訂 view controller 類別 & model 型別。
  • viewDidLoad。
  • IBOutlet & IBAction。
  • IBSegueAction & performSegue。
  • UIAlertController。
  • 可考慮使用的特別語法: enum,computed property,property observer

Apple 官方參考範例

Develop in Swift Explorations 4.3 Building an ElementQuiz App

請 AI 解題

卡關的同學或是已經完成但想跟 AI 比賽,也可以參考以下 AI 的寫法,比看看誰寫得比較好。

以下是彼得潘一連串的提問。

AI 沒有寫出問題的 array,我們請它定義 20 個美食相關的問題。

AI 沒有用到 enum,computed property,property observer,請它再修改。

要求每次玩的時候題目順序跟選項順序都不一樣。

用 alert 顯示答對或答錯了。

經過一連串的溝通,AI 成功地完成了選擇題 App,以下是完整的程式。

  • FoodCategory
enum FoodCategory: String {
case italian, japanese, french, thai, indian, turkish, american, chinese, mexican, greek
}
  • Question
struct Question {
let question: String
let options: [String]
let correctAnswer: Int
let category: FoodCategory
}
  • ViewController
class ViewController: UIViewController {
@IBOutlet weak var questionLabel: UILabel!
@IBOutlet weak var questionNumberLabel: UILabel!
@IBOutlet var optionButtons: [UIButton]!

var questions: [Question] = []
var currentQuestionIndex = 0 {
didSet {
loadQuestion()
}
}
var score = 0

var currentCategory: FoodCategory? {
return questions.isEmpty ? nil : questions[currentQuestionIndex].category
}

override func viewDidLoad() {
super.viewDidLoad()
setupQuestions()
loadQuestion()
}

func setupQuestions() {
questions = [
Question(question: "意大利有名的披薩起源於哪個城市?",
options: ["羅馬", "米蘭", "拿坡里", "佛羅倫薩"],
correctAnswer: 2,
category: .italian),
Question(question: "日本的壽司通常包含哪種魚?",
options: ["鱈魚", "金槍魚", "鮭魚", "吳郭魚"],
correctAnswer: 1,
category: .japanese),
Question(question: "法國知名的可頌麵包主要由哪種材料製成?",
options: ["麵粉、水、酵母和鹽", "麵粉、奶油、糖和酵母", "麵粉、蛋、牛奶和糖", "麵粉、黃油、鹽和糖"],
correctAnswer: 3,
category: .french),
Question(question: "泰國的綠咖哩通常包含哪些香料?",
options: ["香茅、青檸葉、薑黃", "八角、肉桂、丁香", "芫荽、辣椒、薑", "香茅、辣椒、南薑"],
correctAnswer: 3,
category: .thai),
Question(question: "印度的咖喱通常包含哪些香料?",
options: ["肉桂、小茴香、八角", "丁香、肉桂、胡椒", "薑黃、肉桂、辣椒粉", "茴香、薑、辣椒"],
correctAnswer: 2,
category: .indian),
Question(question: "土耳其的土耳其石頭通常是用哪種肉製成的?",
options: ["羊肉", "牛肉", "魚肉", "豬肉"],
correctAnswer: 0,
category: .turkish),
Question(question: "美國的漢堡起源於哪個城市?",
options: ["紐約", "芝加哥", "洛杉磯", "聖路易"],
correctAnswer: 1,
category: .american),
Question(question: "中國的餃子通常包含哪些食材?",
options: ["豬肉、白菜、韭菜", "牛肉、青椒、蘿蔔", "雞肉、玉米、香菇", "蝦肉、芹菜、豆腐"],
correctAnswer: 0,
category: .chinese),
Question(question: "墨西哥的玉米卷中通常包含哪些配料?",
options: ["燻肉、芝士、生菜", "牛肉、番茄、酪梨", "蝦肉、玉米、青椒", "豆泥、辣椒、米飯"],
correctAnswer: 1,
category: .mexican),
Question(question: "希臘的穆薩卡通常包含哪些食材?",
options: ["茄子、馬鈴薯、番茄醬", "西紅柿、黃瓜、羊乳酪", "菠菜、羊肉、酪梨", "羊肉、洋蔥、紅椒"],
correctAnswer: 0,
category: .greek),
Question(question: "以下哪一種食物源自意大利?",
options: ["披薩", "壽司", "泡菜", "燒烤"],
correctAnswer: 0,
category: .italian),
Question(question: "以下哪一種食物源自日本?",
options: ["餃子", "拉麵", "麵包", "沙拉"],
correctAnswer: 1,
category: .japanese),
Question(question: "以下哪一種食物源自法國?",
options: ["法國吐司", "章魚燒", "炒飯", "咖哩"],
correctAnswer: 0,
category: .french),
Question(question: "以下哪一種食物源自泰國?",
options: ["義大利麵", "壽司", "泰式炒飯", "披薩"],
correctAnswer: 2,
category: .thai),
Question(question: "以下哪一種食物源自印度?",
options: ["咖哩", "薯條", "漢堡", "熱狗"],
correctAnswer: 0,
category: .indian),
Question(question: "以下哪一種食物源自土耳其?",
options: ["土耳其石頭", "魷魚丸子", "炸雞", "薯條"],
correctAnswer: 0,
category: .turkish),
Question(question: "以下哪一種食物源自美國?",
options: ["熱狗", "壽司", "意大利麵", "泡菜"],
correctAnswer: 0,
category: .american),
Question(question: "以下哪一種食物源自中國?",
options: ["餃子", "墨西哥捲餅", "綠咖哩", "燉肉"],
correctAnswer: 0,
category: .chinese),
Question(question: "以下哪一種食物源自墨西哥?",
options: ["披薩", "墨西哥玉米卷", "炸薯條", "沙拉"],
correctAnswer: 1,
category: .mexican),
Question(question: "以下哪一種食物源自希臘?",
options: ["燒烤", "希臘沙拉", "壽司", "泡菜"],
correctAnswer: 1,
category: .greek)
]

// 將題目隨機排序
questions.shuffle()

// 遍歷題目,將選項隨機排序
for (index, question) in questions.enumerated() {
var options = question.options
options.shuffle()
let newQuestion = Question(
question: question.question,
options: options,
correctAnswer: options.firstIndex(of: question.options[question.correctAnswer])!,
category: question.category
)
questions[index] = newQuestion
}




}

func loadQuestion() {
if currentQuestionIndex < 10 {
let question = questions[currentQuestionIndex]
questionLabel.text = question.question
questionNumberLabel.text = "題目 \(currentQuestionIndex + 1) / 10"

for (index, optionButton) in optionButtons.enumerated() {
optionButton.setTitle(question.options[index], for: .normal)
}
} else {
showFinishedAlert()
}
}

@IBAction func optionButtonTapped(_ sender: UIButton) {
guard let selectedIndex = optionButtons.firstIndex(of: sender) else { return }

let question = questions[currentQuestionIndex]
let alertController: UIAlertController

if question.correctAnswer == selectedIndex {
score += 10
alertController = UIAlertController(title: "答對了!", message: "你答對了這題,繼續加油!", preferredStyle: .alert)
} else {
let correctAnswerText = question.options[question.correctAnswer]
alertController = UIAlertController(title: "答錯了!", message: "正確答案是:\(correctAnswerText)", preferredStyle: .alert)
}

alertController.addAction(UIAlertAction(title: "繼續", style: .default, handler: { _ in
self.currentQuestionIndex += 1
}))

present(alertController, animated: true, completion: nil)
}


func showFinishedAlert() {
let alertController = UIAlertController(title: "完成", message: "您已完成所有題目!", preferredStyle: .alert)
let okAction = UIAlertAction(title: "確定", style: .default) { _ in
self.performSegue(withIdentifier: "showResultSegue", sender: self)
}
alertController.addAction(okAction)
present(alertController, animated: true)
}


@IBSegueAction func showResultSegueAction(_ coder: NSCoder) -> ScoreViewController? {
let scoreViewController = ScoreViewController(coder: coder)
scoreViewController?.score = self.score
return scoreViewController
}
}
  • ScoreViewController
class ScoreViewController: UIViewController {
@IBOutlet weak var scoreLabel: UILabel!

var score: Int = 0

override func viewDidLoad() {
super.viewDidLoad()
scoreLabel.text = "得分:\(score)"
}

@IBAction func backToQuizButtonTapped(_ sender: UIButton) {
navigationController?.popViewController(animated: true)
}
}

最後我們再設計一下 storyboard 畫面,連結 IBOutlet,IBAction & IBSegueAction 後,一個完整的美食選擇題 App 就完成了 !

作品集

--

--

彼得潘的 iOS App Neverland
彼得潘的 100 道 Swift iOS App 謎題

彼得潘的iOS App程式設計入門,文組生的iOS App程式設計入門講師,彼得潘的 Swift 程式設計入門,App程式設計入門作者,http://apppeterpan.strikingly.com