【Swift5/Xcode】入門編!クイズアプリを作成してみよう!参考書より丁寧に解説します。【Part4】

 

サトリク

前回の講座を受けていない方はこちらから

 

今の状態

サトリク

今の状態は、

  • 「スタート」ボタンを押すと、問題画面に遷移する。
  • 問題画面では、1問目の問題番号と問題文と選択肢4つが表示される。
  • ボタンを押すと、推したボタンの番号がコンソールに表示される。

という感じです。

Part4では一気にクイズアプリらしくなります。コーディングが多いので、慎重にやっていきましょう!

 

 

正誤判定を行う

まずは、選択肢を押したときに、コンソールに正解か不正解かを表示させましょう!

STEP.1
QuizViewController.swiftを開く

左のメニューから、QuizViewController.swiftを選択してください。

STEP.2
ボタンを押した時の正誤判定

ボタンを押した時に呼ばれるブロックを以下のように書き換えましょう。

//ボタンを押したときに呼ばれる
@IBAction func btnAction(sender: UIButton) {
    if sender.tag == Int(quizArray[1]) {
        print("正解")
    } else {
        print("不正解")
    }
}

ここでは、押された番号(sender.tag)と、正解番号(quizArrat[1])を比較しています。

比較して、もし同じだったら「正解」。そうでなかったら不正解と表示させるプログラムです。

STEP.3
実行して確認

左上の再生ボタンを押して実行して確認しましょう。

以下の画像のように、正解のボタンを押すと「正解」と表示され、不正解のボタンを押すと「不正解」と表示されるはずです。

ちなみに、答えは「うし」です。

完了

上記の確認ができたら、正誤判定は完了です。

 

次の問題を表示する

では、問題を解いたら、次の問題を表示するプログラムコードを書いていきたいと思います。

記述するファイルは、QuizViewController.swiftです。

STEP.1
次の問題を表示させるブロックを追記

次の問題を表示させるプログラムを書いていきましょう。

ボタンを押した時に呼ばれるブロック(@IBAction func btnAction)の下に以下のコードを記述しましょう。

func nextQuiz() {
    quizCount += 1
    quizArray = csvArray[quizCount].components(separatedBy: ",")
    quizNumberLabel.text = "第\(quizCount + 1)問"
    quizTextView.text = quizArray[0]
    answerButton1.setTitle(quizArray[2], for: .normal)
    answerButton2.setTitle(quizArray[3], for: .normal)
    answerButton3.setTitle(quizArray[4], for: .normal)
    answerButton4.setTitle(quizArray[5], for: .normal)
}

このコードをざっと解説すると、

現在解いている問題番号を表示するquizCountに1を足します。

全ての問題データが入っているcsvArrayから、quizCount行の問題をquizArrayに代入します。

そのquizArray([問題文,正解番号,選択肢1,選択肢2,選択肢3,選択肢4])をそれぞれLabelやButtonに代入します。

という感じです。

STEP.2
先ほど作成したブロックを呼ぶ

先ほど作成した次の問題を表示するブロックをボタンが押した時に実行するようにしましょう。

ボタンを押したときのブロックの中のif文の下にnextQuiz()を追記します。

//ボタンを押したときに呼ばれる
@IBAction func btnAction(sender: UIButton) {
    if sender.tag == Int(quizArray[1]) {
        print("正解")
    } else {
        print("不正解")
    }
    nextQuiz()
}

これで、ボタンを押した後に正誤判定をしてから、次の問題を表示します。

STEP.3
実行して確認

左上の再生ボタンを押して実行して確認しましょう。

このように、選択肢ボタンを押すと次の問題がセットされることを確認してください。

がしかし、ここで2点問題が見つかりました。

問題①:Labelの幅が狭すぎて文字が…になってしまう。

問題②:5問以上行うと強制終了してしまう。

完了

サトリク

では、この問題を解決していきましょう!

 

問題①解決方法:オブジェクトの幅を広げる

文字が…になるのはLabelの幅が狭いからです。

単純にLabelの幅を広げてあげましょう。

STEP.1
Main.storyboardを選択

左のアイコンをクリックして、Main.storyboardを選択

STEP.2
使わないウィンドウは非表示

今はStoryboad上をいじるので、コード画面が邪魔です。なので以下のように使わないウィンドウは非表示にしましょう。

STEP.3
Labelの幅を広げる

以下のようにLabelの幅を広げましょう。

①Labelを横いっぱいに広げる

②右側のウィンドウが表示されていない場合は、右上のアイコンをクリック

(Show the Attributes inspector)を選択

Alignmentを中央揃えに変更

STEP.4
Buttonの幅を広げる

Buttonの幅も横いっぱいに広げましょう

地味ですが、一つ一つ広げてください。

STEP.5
実行して確認

左上の再生ボタンを押して実行して確認しましょう。

このようにLabelやButtonの文字が…になっていなければOKです

完了

これで問題①は解決しました。

 

問題②解決方法:全て終わったらスコア画面に遷移させる

現状問題が全て終わると、アプリが強制終了してしまいます。

理由は、最後の問題の5問目が終わって、6番目の問題を取り出そうとした時に、6番目の問題がないのでエラーになってしまっています。

なので、用意する問題がなくなったら、スコア画面に遷移するような処理を加えてあげましょう。

STEP.1
セグエで紐づける

まず、セグエで紐付けましょう。

選択肢ボタンを押した時に遷移するのですが、今回は、条件が満たされた時のみ遷移させるようにします。

問題画面のバーの左側の  からcontrolを押しながら、スコア画面にドラッグ&ドロップ

 

左のようなウィンドウが表示されるので、「Present Modally」を選択

STEP.2
SegueにIDをつける

SegueにIDをつけましょう。

①Segueを選択

② (Show the Attributes inspector)を選択

③IdentifierにtoScoreVCと入力

STEP.3
スコア画面をフルスクリーンにする

このままでは、スコア画面に遷移した時、上に隙間ができる形の画面遷移になってしまいます。フルスクリーンに設定しましょう。

①スコア画面の上のバーをクリック

② (Show the Attributes inspector)を選択

③Presentationを「Full Screen」に変更

STEP.4
QuizViewController.swiftを開く

では、問題が全て終わった場合に、先ほど設定したSegueを、呼び出す処理を書いていきます。

左のメニューから、QuizViewController.swiftを選択してください。

STEP.5
全ての問題が終わった場合、遷移させる

nextQuiz()ブロックを以下のように変更しましょう。

func nextQuiz() {
    quizCount += 1
    if quizCount < csvArray.count {
        quizArray = csvArray[quizCount].components(separatedBy: ",")
        quizNumberLabel.text = "第\(quizCount + 1)問"
        quizTextView.text = quizArray[0]
        answerButton1.setTitle(quizArray[2], for: .normal)
        answerButton2.setTitle(quizArray[3], for: .normal)
        answerButton3.setTitle(quizArray[4], for: .normal)
        answerButton4.setTitle(quizArray[5], for: .normal)
    } else {
        performSegue(withIdentifier: "toScoreVC", sender: nil)
    }
}

if文で、現在の問題カウント数が、問題数より小さかったら次の問題を表示して、そうでなかったら、スコア画面に遷移するという処理に変更しました。

STEP.6
実行して確認

左上の再生ボタンを押して確認してみましょう。

問題を全て解くと、スコア画面に遷移するはずです。

完了

これで問題②は解決しました。

 

ここまでの全コード

import UIKit

class QuizViewController: UIViewController {
    @IBOutlet var quizNumberLabel: UILabel!
    @IBOutlet var quizTextView: UITextView!
    @IBOutlet var answerButton1: UIButton!
    @IBOutlet var answerButton2: UIButton!
    @IBOutlet var answerButton3: UIButton!
    @IBOutlet var answerButton4: UIButton!
    
    var csvArray: [String] = []
    var quizArray: [String] = []
    var quizCount = 0

    override func viewDidLoad() {
        super.viewDidLoad()
        
        csvArray = loadCSV(fileName: "quiz")
        print(csvArray)
        
        quizArray = csvArray[quizCount].components(separatedBy: ",")
        quizNumberLabel.text = "第\(quizCount + 1)問"
        quizTextView.text = quizArray[0]
        answerButton1.setTitle(quizArray[2], for: .normal)
        answerButton2.setTitle(quizArray[3], for: .normal)
        answerButton3.setTitle(quizArray[4], for: .normal)
        answerButton4.setTitle(quizArray[5], for: .normal)

        // Do any additional setup after loading the view.
    }
    
    //ボタンを押したときに呼ばれる
    @IBAction func btnAction(sender: UIButton) {
        if sender.tag == Int(quizArray[1]) {
            print("正解")
        } else {
            print("不正解")
        }
        nextQuiz()
    }
    
    func nextQuiz() {
        quizCount += 1
        if quizCount < csvArray.count {
            quizArray = csvArray[quizCount].components(separatedBy: ",")
            quizNumberLabel.text = "第\(quizCount + 1)問"
            quizTextView.text = quizArray[0]
            answerButton1.setTitle(quizArray[2], for: .normal)
            answerButton2.setTitle(quizArray[3], for: .normal)
            answerButton3.setTitle(quizArray[4], for: .normal)
            answerButton4.setTitle(quizArray[5], for: .normal)
        } else {
            performSegue(withIdentifier: "toScoreVC", sender: nil)
        }
    }
    
    func loadCSV(fileName: String) -> [String] {
        let csvBundle = Bundle.main.path(forResource: fileName, ofType: "csv")!
        do {
            let csvData = try String(contentsOfFile: csvBundle,encoding: String.Encoding.utf8)
            let lineChange = csvData.replacingOccurrences(of: "\r", with: "\n")
            csvArray = lineChange.components(separatedBy: "\n")
            csvArray.removeLast()
        } catch {
            print("エラー")
        }
        return csvArray
    }

    /*
    // MARK: - Navigation

    // In a storyboard-based application, you will often want to do a little preparation before navigation
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        // Get the new view controller using segue.destination.
        // Pass the selected object to the new view controller.
    }
    */

}

 

クイズアプリを作ろうPert4まとめ

 

まとめ
  • Labelの幅が狭いと文字…になってしまう。
  • 条件によって遷移させたい時は、 からcontrolを押しながら遷移させたい画面にドラッグ&ドロップする
  • performSegue(withIdentifier: "SegueにつけたID", sender: nil)で画面遷移させる

 

一緒に楽しく雑談しながらアプリ開発しませんか?

RikutoSato

satorikublogの筆者がアプリ開発をマンツーマンでサポートします。

あなたのクイズアプリをAppStoreにリリースするまで、チャットや、ビデオ通話で楽しく雑談でもしながらサポートします。エラーで先に進まない方や、アイデアはあるけど、そのアイデアをアプリに実現できない方など、気軽にご相談ください!

詳しくはこちら