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

サトリク

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

 

今の状態

サトリク

今の状態は、

  • スタートボタンを押すと、1問目の問題番号と問題文と選択肢4つが表示される。
  • ボタンを押すと、コンソールに番号が表示される。

という感じです。

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

 

 

ボタンを押したときに正誤判定を行う

ボタンを押したときに、正解か不正解かをコンソールに表示させたいと思います。

STEP.1
QuizViewControllerを開く

左側のメニューから、QuizViewController.swiftをダブルクリックする

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

ボタンを押したときに呼ばれるメソッドが40行目あたりにあります。ここの中を以下のように書き換えてください

 

@IBAction func btnAction(sender: UIButton) {
    if sender.tag == Int(quizArray[1]) {
        print("正解")
    } else {
        print("不正解")
    }
}

ここでは、何番目のボタンが押されたかを判定していた、Pert2でつけたsender.tagと、
quizArray[1]つまり、問題データ(問題文0,正解番号1,選択肢12,選択肢23,選択肢34,選択肢4)の1番の正解番号を比較して、同じだったら、コンソールに正解と表示。そうでなかったら不正解と表示するという処理です。

ちょっとこんがらがる処理ですが、落ち着いてゆっくり考えれば理解できます。

 

ふりがなをふるとこんな感じです。

ifもし sender.tag押されたボタンの番号 ==同じだったら Int数字の(quizArray[1]正解番号) {
    print(“正解”)正解とコンソールに表示
} elseそうでなければ {
    print(“不正解”)不正解とコンソールに表示
}

STEP.3
実行して確認

左上のボタンを押すか、command + rで実行しましょう。

正解のボタンを押すと、コンソールに正解と表示され、不正解のボタンを押すと不正解と表示されるはずです。

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

 

次の問題を表示する

次はボタンを押したら、次の問題を表示するプログラムを記述していきたいと思います。

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

STEP.1
次の問題を表示するプログラム

48行目あたり(先ほど記述した、ボタンの正誤判定の下)に以下のコード記述してください。

 

func nextQuiz() {
    quizCount += 1
    quizArray.removeAll()
    quizArray = csvArray[quizCount].components(separatedBy: ",")
    quizNumberLabel.text = "第\(quizCount + 1)問"
    quizTextView.text = quizArray[0]
    answerBtn1.setTitle(quizArray[2], for: .normal)
    answerBtn2.setTitle(quizArray[3], for: .normal)
    answerBtn3.setTitle(quizArray[4], for: .normal)
    answerBtn4.setTitle(quizArray[5], for: .normal)
}

funcから始まる処理は、関数と言ってひとまとまりの処理です。ここでは、現在表示されているクイズを一旦消して、次の問題のクイズをLabelやボタンに表示させている処理を行っています。
つまり、問題の入れ替えをしています。

ただし、関数はただ記述しただけでは実行されないので、どこかで呼ばなければなりません。

STEP.2
関数を呼ぶ

先ほど書いた関数を、ボタンを押したときに実行されるようにしたいので、

46行目あたりのボタンを押したときの処理の中にnextQuiz()を追記します。

 

@IBAction func btnAction(sender: UIButton) {
    if sender.tag == Int(quizArray[1]) {
        print("正解")
    } else {
        print("不正解")
    }
    nextQuiz()
}

正誤判定した後、nextQuiz()をしています。

nextQuiz()と記述すると、先ほど記述した関数が実行されます。

そのため、これで、ボタンを押した瞬間に、正誤判定をしてクイズを入れ替えるという処理が完成しました。

STEP.3
実行して確認

左上のボタンを押すか、command + rで実行しましょう。

ボタンを押すと、コンソールに正解、不正解が表示され、次の問題がセットされることを確認しましょう。

がしかし、ここで見つかった問題が2点

① Labelの幅が狭すぎて文字が…になる

② 5問以上行うと、アプリが強制終了してしまう。

完了

サトリク

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

 

オブジェクトの幅を広げる

先ほど実行して見つかった、Labelの幅が狭すぎて文字が…になる問題を解決しましょう。

STEP.1
Main.storyboardを開く

①ナビゲーションエリアを表示

 (show the Project navigator)を選択

③Main.storyboardを選択

STEP.2
Labelの幅を広げる

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

(Show the Attributes inspector)を選択する

③Alignmentを中央揃えにする

STEP.3
Buttonの幅を広げる

同じくButtonの幅も横いっぱいに広げましょう。

STEP.4
実行して確認

左上のボタンを押すか、command + rで実行して問題数や、選択肢が…にならないことを確認してください。

完了

うまく表示できていたら完了です。

 

全て終わったらスコア画面に遷移させる

今の段階では、CSVファイルに記述している問題が全て終わると強制終了してしまいます。なので、問題が全て終わったら、スコア画面に遷移するようにしましょう。

STEP.1
画面遷移させる

Part2で「スタートボタン」を押したら、問題画面に遷移するという処理を行いました。その時は、スタートボタンからcontrolを押しながら問題画面にドラッグ&ドロップしました。

ですが、今回は四つのボタンどのボタンを押しても全ての問題が終わったら、画面遷移させるという、場合によって画面遷移するかどうか決めます。そのような場合は、以下のように接続します。

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

 

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

そうすると、Segueができます。このSegueを処理で呼び出すと画面遷移が実行されます。

そのため、まずはSegueにIDをつけます。

STEP.2
SegueにIDをつける

①Segueを選択

(Show the Attributes inspector)を選択する

③IdentifierにscoreVCと入力する

これでSegueにIDをつけることができました。なのであとは処理で呼び出すだけです。

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

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

①スコア画面の  を選択

(Show the Attributes inspector)を選択する

③PresentationをFull Screenにする

STEP.4
QuizViewControllerを開く

では、次は、QuizViewController.swiftで、問題が全て終わったら画面遷移するという処理を追記していきます。

左のメニューからQuizViewController.swiftをダブルクリックします。

STEP.5
問題数を入れる変数を用意

まずは、問題数を入れる変数を用意します。

22行目あたり、var quizCount = 0の下に以下のコードを追記してください。

 

var quizTotal = 5

今回は、5問しか問題数がないので、5を代入します。※自分で問題を作ってる人は問題の数だけ入力してください。

STEP.6
画面遷移する処理

50行目あたりのfunc nextQuiz() {を以下のように書き換えましょう。

func nextQuiz() {
    quizCount += 1
    quizArray.removeAll()
    if quizCount < quizTotal {
        quizArray = csvArray[quizCount].components(separatedBy: ",")
        quizNumberLabel.text = "第\(quizCount + 1)問"
        quizTextView.text = quizArray[0]
        answerBtn1.setTitle(quizArray[2], for: .normal)
        answerBtn2.setTitle(quizArray[3], for: .normal)
        answerBtn3.setTitle(quizArray[4], for: .normal)
        answerBtn4.setTitle(quizArray[5], for: .normal)
    } else {
        performSegue(withIdentifier: "scoreVC", sender: nil)
    }
}

func関数 nextQuiz() {
    ~~省略~~
    ifもし quizCount現在、出したクイズの数が <より小さかったら quizTotal問題数 {
        quizArray = csvArray[quizCount].components(separatedBy: “,”)次の問題を表示
        ~~省略~~
    } elseそうでなかったら {
        performSeguesegue実行(withIdentifier: “scoreVC”IDがscoreVCの, sender: nil値の受け渡しなし)
    }
}

 

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

STEP.7
実行して確認

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

完了

サトリク

上記のことが確認できたら、Part4は終了です!

 

ここまでの全コード

注意

丸々コピペしても、エラーが出る場合は、もう一度Part1からやり直した方がいいかもしれません。

Xcodeになれていない状態で、修正するのはかなり至難の技ですので、今なら最初からやった方が早いと思います。

//
//  QuizViewController.swift
//  SampleQuiz
//
//  Created by RikutoSato on 2020/10/22.
//

import UIKit

class QuizViewController: UIViewController {
    
    @IBOutlet weak var quizNumberLabel: UILabel!
    @IBOutlet weak var quizTextView: UITextView!
    @IBOutlet weak var answerBtn1: UIButton!
    @IBOutlet weak var answerBtn2: UIButton!
    @IBOutlet weak var answerBtn3: UIButton!
    @IBOutlet weak var answerBtn4: UIButton!
    
    var csvArray: [String] = []
    var quizArray: [String] = []
    var quizCount = 0
    var quizTotal = 5
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        csvArray = loadCSV(fileName: "quiz")
        print(csvArray)
        
        quizArray = csvArray[quizCount].components(separatedBy: ",")
        quizNumberLabel.text = "第\(quizCount + 1)問"
        quizTextView.text = quizArray[0]
        answerBtn1.setTitle(quizArray[2], for: .normal)
        answerBtn2.setTitle(quizArray[3], for: .normal)
        answerBtn3.setTitle(quizArray[4], for: .normal)
        answerBtn4.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
        quizArray.removeAll()
        if quizCount < quizTotal {
            quizArray = csvArray[quizCount].components(separatedBy: ",")
            quizNumberLabel.text = "第\(quizCount + 1)問"
            quizTextView.text = quizArray[0]
            answerBtn1.setTitle(quizArray[2], for: .normal)
            answerBtn2.setTitle(quizArray[3], for: .normal)
            answerBtn3.setTitle(quizArray[4], for: .normal)
            answerBtn4.setTitle(quizArray[5], for: .normal)
        } else {
            performSegue(withIdentifier: "scoreVC", 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")
        } 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まとめ

 

まとめ
  • func 〇〇()から始まる処理は、関数という
  • 〇〇()と記述すると、先ほど記述した関数が実行される
  • ある条件によって、画面遷移するかしないかを決める際は、Storyboardの上のバーの をドラッグ&ドロップする
  • プログラムでperformSegue(withIdentifier: "score", sender: nil)を記述すると画面遷移する

 

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

RikutoSato

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

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

詳しくはこちら