[iOSアプリ開発] クイズアプリ

iPhoneで動くオリジナルな「クイズアプリ」(2択/3択)を作成しませんか?

初心者の方でも簡単に作れる内容です。

このアプリは問題に対する回答(2択/3択)を選択すると、正解の場合は緑色、不正解の場合は赤色に変色します。タイマーを使って変色する時間を調整します。

目次

概要

この講座で学習する内容
  • クイズアプリを作成

アプリを作成する過程で、以下の内容を学習できます。

  • MVC(モデル・ビュー・コントローラー)デザインパターン
    プログラムを役割に応じて3分割する方法
  • 配列を構造体(struct)に移行する方法
  • 構造体の使い方
  • 構造体のプロパティを変更する方法
  • 戻り値がある関数の使い方
  • プログレスビューの使い方
  • タイマーの使い方

MVCデザインパターンを適用しています。
Model(M)、View(V)、Controller(C
記事中、M、V、Cの略を使用しています。

参考にした講座】iOS & Swift – The Complete iOS App Development Bootcamp (Udemy)
 (Section9) iOS App Design Patterns and Code Structuring

この講座で作成するアプリの概要
  • 画面中央に問題が表示されます。
  • 3つの選択肢から回答を選択します。
  • 正解の場合はボタンが緑色に、不正解の場合はボタンが赤色に、0.2秒間だけ変わります。
  • また、正解の場合は、画面上部のスコアが1ポイント加算されます。
  • 問題の回答後、次の問題が表示されます。
  • すべての問題の回答が終わると、最初の問題に戻ります。その際、スコアも0に初期化されます。
アプリの開発環境
  • Xcode: Version 14.2
  • macOS: Venture: Version 13.2.1
  • iOS: 16.4.1
GitHubからプロジェクトを入手したい方はこちら
Xcodeのインストールが未了の方はこちら
GitHubからXcodeプロジェクトをクローンする方法はこちら

GitHubにアクセス後、「Code」をクリックし、赤枠で囲まれたURLをコピーします。

Xcodeを起動後、赤枠で囲まれた「Clone an existing project」をクリックします。

先ほどコピーしたURLを入力箇所にペーストし、Cloneボタンをクリックすれば、クローン完成です。

UIの設定

開始プロジェクトをクローンして起動した状態を確認してみましょう。

開始プロジェクトの確認

Mainを開くと、濃い青色の背景の上に、タイトルラベル「Question Text」、画面下部に「True」「False」ボタン、プログレスビューが既にあることが確認できます。

しかし、ViewControllerは初期画面のままであるため、Assistantを開いて、タイトルラベルやボタンなどをViewControllerから操作できるようにリンクを作成します。

UIとコードのリンク

「True」「False」ボタンについては、どちらかのボタンが押された時にトリガーされるIBActionを追加しています。

ボタンを押すと正誤判定し、次の質問へ更新する仕組みの作成

問題・解答を2次元配列に格納

ボタンを押すと次の質問に更新するコードの作成

簡単な質問を3つ作成し配列に格納します。アプリ起動後、1つ目の質問が表示され、ボタンを押すと次の質問に更新されるコードを記載します。

ボタンを押すと、questionNumber+=1により、次の質問に更新される

問題を回答後、正誤判定(print出力)するコードの作成

質問の回答は「True」「False」ボタンを押して行います。

押されたボタンに表示されている「True」「False」の文字列が問題の回答と合致するか判定し、その結果を出力します。

問題に対する回答は、2次元配列を使用して作成します。

ボタンを押すと、ボタンに書かれたTrueまたはFalseの文字が、quiz配列のTrueまたはFalseと合致するか確認

現在、質問は3つしかないため、それを超えてボタンをクリックすると、アプリがクラッシュすることになります。

そこで、最後の問題まで到達すると、最初の問題に戻るようにコードを追加します。

修正前
questionNumber += 1
    
修正後
if questionNumber + 1 < quiz.count {
    questionNumber += 1
} else {
    questionNumber = 0
}

問題・解答を構造体に格納

問題数が増えてもデータ管理が容易にできるように、問題・解答を格納している配列を構造体に変更してみましょう。

構造体は、新しいSwiftファイルを作成し、Questionという名前にします。

変更前(配列)
変更後(構造体)
quiz[questionNumber][0]→quiz[questionNumber].text,
quiz[questionNumber][1]→quiz[questionNumber].answerに変更

配列が構造体に変わると、問題や解答を要求するためのコマンドの記載方法が変わるこも注意が必要!(図中の黄色マーカを参照)

構造体にイニシャライザを設定する

構造体にイニシャライザを設定し、コードをさらにシンプルにします。また、問題数を追加しました。

構造体にイニシャライザを設定
struct Question {
    let text: String
    let answer: String
    
    init(q: String, a: String) {
        text = q
        answer = a
    }
}
let quiz = [
    Question(q: "4+2 = 6", 
        a: "True"),
    Question(q: "5-3 > 1", 
        a: "True"),
    Question(q: "3+8 < 10", 
        a: "False"),
    Question(q: "日本の首都は東京", 
        a: "True"),
    Question(q: "アメリカの首都はニューヨーク", 
        a: "False")
]

正誤判定をボタンの背景色を変更して判別する

これまでは、正誤判定をprint分の出力により確認していたが、これではその内容をプログラム開発者しか知ることができない。

ボタンの背景色を変更する

そこで、正誤判定をボタンの背景色を変更(正解→緑、不正解→赤)することで判別できるように変更する。

正誤判定でボタンの背景色を変更
if userAnswer == actualAnswer {
//正解→緑
    sender.backgroundColor = 
        UIColor.green
} else {
//不正解→赤
    sender.backgroundColor = 
        UIColor.red 
}

ボタンの背景色を0.2秒後にクリアする

ボタンの背景色を変更することができたが、次の質問を回答する前に背景色をクリアして次の問題に備える必要があります。

正誤判定して0.2秒後に背景色がクリアされる

アプリ完成までの各種設定

プログレスバーを設置して、問題の進行状況を確認する

プログレスバーを設置
@objc func updateUI() {
    questionLabel.text = 
        quiz[questionNumber].text
    trueButton.backgroundColor = 
        UIColor.clear
    falseButton.backgroundColor = 
        UIColor.clear
//問題の進捗状況を計算
    progressBar.progress = 
        Float(questionNumber + 1) 
        / Float(quiz.count)
}

progressBar.progress = …」の1行追加で、プログレスバーで問題の進捗状況が確認できるようになりました。
画像は4/5問目→プログレスバーが80%進捗していることがわかります。

MVCデザインパターンの実装

現在、アプリは正常に動作し、必要な機能(正誤判定、進捗状況の確認)は使えていますが、プログラムコードが良い状態にあるとは言えません。

なぜなら、すべてのロジックがViewCntrollerに詰め込まれているため、とても読みにくい状態です。今は、プログラムが短くこのままでもなんとかなります。

しかし、今後プログラムが長文になることを想定し、プログラムを効率的に作成したり、読みやすくするため、Appleが推奨するMVC(モデル・ビュー・コントローラー)を実装します。

MVCデザインパターンの実装

MVCを実装するため、ViewController(C)の中にあった、以下の機能をQuizBrain(M:新設)に移行しました。

  1. 正誤判定(赤字)→checkAnswer()に移行
  2. 次の質問へ進行→nextQuestion() に移行
    構造体の中にあるプロパティを変更するため、メソッドの前に「mutating」を付す
  3. 質問入手をメソッドgetQuestionText()に移行
  4. 進捗状況入手をメソッドgetProgress()に移行

その結果、ViewControllerには、QuizBrainに移行したメソッドを実行する行のみが残り、プログラムが見やすくなった。

画面上部にスコア(正答数)を表示

画面上部に問題のスコア(正答数)を表示するため、以下の手順でコードを追加します。

  • 画面上部にLabelを追加(V)し、ViewController(C)とリンクを作成
  • QuizBrain(M)の中に、メソッドgetScore()を作成
  • ViewController(C)からメソッドgetScore()を指示し、QuizBrain(M)からスコアを取得
  • ViewController(C)は取得したスコアを追加したラベル(V)に表示
画面上部にスコア(正答数)を表示
//ViewController 抜粋
@objc func updateUI() {
     scoreLabel.text = "Score: \(quizBrain.getScore())"
}
//QuizBrain抜粋
var score = 0
mutating func checkAnswer(_ userAnswer: String) 
    -> Bool {
    if userAnswer == quiz[questionNumber].answer {
        score += 1
        return true
    } else {
        return false
    }
}
func getScore() -> Int {
    return score
}

3択問題に変更

quiz配列を次の3択問題に変更した場合の、プログラムコードを変更してみます。

let quiz = [
    Question(q: "日本の首都は?", 
             a: ["東京", "大阪", "名古屋"], 
             correctAnswer: "東京"),
    Question(q: "中国の首都は?", 
             a: ["上海", "香港", "北京"], 
             correctAnswer: "北京"),
    Question(q: "米国の首都は?", 
             a: ["ニューヨーク", "ホノルル", "ワシントン"], 
             correctAnswer: "ワシントン"),
    Question(q: "韓国の首都は?", 
             a: ["ジャカルタ", "ソウル", "平壌"], 
             correctAnswer: "ソウル"),
    Question(q: "タイの首都は?", 
             a: ["バンコク", "マニラ", "ハノイ"], 
             correctAnswer: "バンコク")
]
変更後(Question.swift)
import Foundation

struct Question {
    let text: String
    let answer: [String]
    let rightAnswer: String
    
    init(q: String, 
         a: [String], 
         correctAnswer: String) {

        text = q
        answer = a
        rightAnswer = correctAnswer
    }
}

3択問題のquiz配列に対応するように、Question.swift(M)を変更

変更後(Main.storyboard)

3択問題に対応するようにMain(V)を変更し、ViewController(C)へリンク

変更後(ViewController, QuizBrain)

ViewController(C)において、QuizBrain(M)のgetAnswers()から3つの選択肢を入手し、それらをMain(V)のボタンのタイトルに設定している。

QuizBrain(M)の中にメソッドgetAnswers()を新たに作成し、3択問題の3つの選択肢を配列で返している。

この記事が気に入ったら
いいね または フォローしてね!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次