#3, iOS 資料傳遞MCQ

Syuan
彼得潘的 Swift iOS / Flutter App 開發教室
11 min readOct 27, 2022

撿了一個農場心理測驗當作內容來簡單練習,不作不曉得一做快忘光...

總之開始吧。

  1. 一樣先把東西先建出來:
ViewController, Button, Label

這裡翻頁使用 Present modally, Full Screen

2.Model

2-1, Question定義資料型別

選項做成陣列

(後來發現其實不用陣列,不過算了就當作以後遇到其他情況會用到)

struct Question {var question: Stringvar options: [String]}

然後做題庫,因為題目不多所以直接放在同一個檔案了

class questionData {var questionContents = [Question(question: "我傾向於通過操縱別人,來達成自己的目的。", options: ["1.非常不同意","2.比較不同意","3.有點不同意","4.中立","5.有點同意","6.比較同意","7.非常同意"]),//礙於篇幅中間省略Question(question: "我希望別人注意我。", options: ["1.非常不同意","2.比較不同意","3.有點不同意","4.中立","5.有點同意","6.比較同意","7.非常同意"]),]}

2-2, Game

Question:遊戲開始時傳入的題目

updateQuestion:用questionIndex當索引,拿出questions裡面的題目跟選項。

class Game {var questions: [Question]?var question: String?var options = [String]()var questionIndex = 0 {didSet {updateQuestion()}}//更新題目func updateQuestion() {if let questions {question = questions[questionIndex].questionoptions = questions[questionIndex].options}}//更新畫面func updateUI(questiontitle:UILabel?, questionNumber: UILabel, optionsButtons:[UIButton]) {questiontitle?.text = questionquestionNumber.text = "#\(questionIndex + 1)"//        options.shuffle()for (i, option) in optionsButtons.enumerated() {option.configuration?.attributedTitle = AttributedString(options[i])}}}

3.Controller

3-1, 從首頁開始做

最尾頁要能夠跳回首頁,所以這裡先做個unwind Segue

//unwind segue, 從結果返回首頁,不傳遞數據所以括號留空@IBAction func unwindToHomeViewController(_ unwindSegue: UIStoryboardSegue){}

製作題目

//裝資料用
let questions = questionData()
//裝題目用陣列
var questionArr = [Question]()

傳遞資料

把Button拉出的Segue再拖曳進Controller

@IBSegueAction func passQuestions(_ coder: NSCoder) -> QuestionViewController? {//題目順序隨機化後加入陣列
questions.questionContents.shuffle()
for quiz in 0...11 {
questionArr.append(questions.questionContents[quiz])
}
//宣告傳遞目標的controller,然後傳陣列過去
let controller = QuestionViewController(coder: coder)controller?.questionArr = questionArr
//傳過去後清除陣列,防止內容累加
questionArr.removeAll()
return controller
}

3-2, 題目頁面

按鈕的outlet先拉一拉,按鈕記得要拉成陣列

@IBOutlet weak var scoreLabel: UILabel!
@IBOutlet weak var questionLabel: UILabel!
@IBOutlet var optionButtons: [UIButton]!
@IBOutlet weak var questionNumLabel: UILabel!

需要用的變數先宣告

//接收從首頁傳來的題目
var questionArr = [Question]()
//生成遊戲物件
let game = Game()
//分數改變後(didset)顯示到label上
var score: Int = 0 {
didSet {
scoreLabel.text = "\(score)"
}
}

接收到題目後要放到 ViewDidLoad讓它顯示

override func viewDidLoad() {
super.viewDidLoad()
//把game物件拿到的題目顯示到對應的outlet
game.questions = questionArr
game.updateQuestion()
game.updateUI(questiontitle: questionLabel, questionNumber: questionNumLabel, optionsButtons: optionButtons)
}

處理選項按鈕,一樣每個按鈕都要拉成一個陣列IBAction

@IBAction func selectAnswer(_ sender: UIButton) {//題目數小於總題目數之前都要計算分數
if
game.questionIndex < 11 {
switch sender {
//sender對應optionButtons[i]
case optionButtons[0]:
score += 1
case optionButtons[1]:
score += 2
case optionButtons[2]:
score += 3
case optionButtons[3]:
score += 4
case optionButtons[4]:
score += 5
case optionButtons[5]:
score += 6
case optionButtons[6]:
score += 7
default:
break
}
//每次按下都要把題目數加1
game.questionIndex += 1
self.game.updateUI(questiontitle: self.questionLabel, questionNumber: self.questionNumLabel, optionsButtons: self.optionButtons)} else {//題目跑完就可以呼叫下一頁,並把題目數歸零,且更新題目畫面
self
.performSegue(withIdentifier: "Result", sender: nil)
game.questionIndex = 0self.game.updateUI(questiontitle: self.questionLabel, questionNumber: self.questionNumLabel, optionsButtons: self.optionButtons)}
}

計算完的分數要傳到下一頁顯示結果

@IBSegueAction func showResult(_ coder: NSCoder, sender: Any?, segueIdentifier: String?) -> ResultViewController? {let controller = ResultViewController(coder: coder)controller?.finalscore = self.scorereturn controller}

3–3, 結果頁面

一樣outlet先拉好

@IBOutlet weak var scoreLabel: UILabel!
@IBOutlet weak var resultLabel: UILabel!

題目頁面得到的分數總和這邊要宣告變數接起來

var finalscore: Int?

做個function判斷接收到的分數,之後記得裝到viewDidLoad顯示

func showResult() {if let finalscore {
scoreLabel.text = "\(finalscore)"
switch finalscore {case 36..<40:
resultLabel.text = "自戀:特別自負且有優越感,認為自己高人一等,應該要享受比別人更好的待遇。"
case 40..<45:
resultLabel.text = "心理變態:高度衝動、追求刺激且缺乏同理心,無法感知別人的情緒。就算看到別人滿臉痛苦,你也沒有感覺。"
case 46..<84:
resultLabel.text = "馬基維利主義:認為成功的關鍵在於操縱、利用他人,為達目的不擇手段、不講道德與原則。同時認為人都是自私自利的,不相信人是善良與美好的。"
default:
resultLabel.text = "有誠實作答嗎?"
}
}

最後作答完需要跳回首頁,由於剛剛已經在首頁做好了unwind segue,所以這邊再把返回的 Button 拖移到controller上方的exit,這樣按鈕就可以使用了。

4.簡單的漸層上色美化

在 Game.swift 加一個 extension 對應所有的 Controller, 裡面放個 function對 Controller 整個View上漸層背景

extension UIViewController{func setupGradientBackground() {
let gradientLayer = CAGradientLayer()
gradientLayer.frame = view.bounds
gradientLayer.colors = [
CGColor(srgbRed: 255, green: 255, blue: 255, alpha: 1),
CGColor(srgbRed: 0, green: 0, blue: 0, alpha: 1)
]
view.layer.insertSublayer(gradientLayer, at: 0)
}
}

以上完成

--

--