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

サトリク

この記事は、Pert6です。前編を読んでいない方は、上記の記事から読んでください。

今の状態

  • スタート画面でスタートを押すと、問題画面に遷移する
  • 問題画面で選択肢を押すと、コンソール上で正誤判定が行われ、次の画面問題がセットされる
  • 5問以上解くと、スコア画面に遷移する
  • スコア画面に正解数が表示される
  • 「トップに戻る」ボタンを押すとスタート画面に戻る

サトリク

今の状況はざっとこんな感じです。

Part6では、選択肢を押したときの正解判定をアプリ上で行うようにしたいと思います!

少し、超初心者にはわかりづらいコードを書くので、あまり詳しい解説はしません。

少し駆け足になってしまいますが、お許しください。

 

 

答えを表示するViewを作成する

まずは、答えを表示するViewを作成します。

STEP.1
QuizViewController.swiftを表示

  (show the Project navigator)を選択

QuizViewController.swiftを選択

STEP.2
UIView、変数の宣言

25行目あたり(var correctCount = 0の下)に以下のコードを記述します。

var explanetionBGView = UIView()
var explanationBGX = 0.0

 

ここでは、UIView()を生成しています。

STEP.3
ViewDidLoadメソッドを書き換え

28行目あたりのViewDidLoadメソッドを以下のように書き加えてください。

override func viewDidLoad() {
    super.viewDidLoad()

    let screenSize:CGSize = UIScreen.main.bounds.size
    explanationBGX = Double(screenSize.width/2) - 320/2
    explanetionBGView.backgroundColor = UIColor.lightGray
    explanetionBGView.frame = CGRect(x: explanationBGX,y:Double(screenSize.height), width: 320, height:  210)
    explanetionBGView.isUserInteractionEnabled = true
    self.view.addSubview(explanetionBGView)

    csvArray = loadCSV(fileName: "quiz")
    //以下省略
}

 

ここでは、Viewのサイズ、位置などを決めています。

STEP.4
explanationメソッドを追加

64行目あたり(btnActionメソッドの中ではなく下)に、以下のメソッドを記述しましょう。

func explanation() {
    let answerBtnY = answerBtn1.frame.origin.y
    UIView.animate(withDuration: 0.5, animations: {() -> Void in self.explanetionBGView.frame = CGRect(x: self.explanationBGX, y: Double(answerBtnY), width: 320, height: 280)
    })
    answerBtn1.isEnabled = false
    answerBtn2.isEnabled = false
    answerBtn3.isEnabled = false
    answerBtn4.isEnabled = false
}

 

STEP.5
explanationメソッドの呼び出し

64行目あたりのbtnActionメソッドのnextQuiz()explanation()に変更しましょう。

@IBAction func btnAction(sender: UIButton) {
    if sender.tag == Int(quizArray[1]) {
        correctCount += 1
        print("正解")
    } else {
        print("不正解")
    }
    print("スコア:\(correctCount)")
    explanation()
}

 

STEP.6
実行してみる

実行して確認しましょう。

選択肢を押すとグレーのViewが出てくるはずです。

完了

確認できたら、次に進みましょう。

今度は、このViewに答えと、ボタンを設置していきたいと思います。

Viewに正解を表示する

先ほど表示させたViewに正解を表示したいと思います。

STEP.1
UILabel()の宣言

27行目あたり(var explanationBGX = 0.0の下)に以下の宣言を記述しましょう。

var correctLabel = UILabel()

 

STEP.2
ViewDidLoadメソッドを書き換える

29行目あたりのViewDidLoadメソッドを以下のように書き加えてください。

override func viewDidLoad() {
    super.viewDidLoad()

    //省略
    self.view.addSubview(explanetionBGView)

    correctLabel.frame = CGRect(x: 60, y:  100, width: 300, height: 30)
    correctLabel.font = UIFont.systemFont(ofSize: 20)
    explanetionBGView.addSubview(correctLabel)

    csvArray = loadCSV(fileName: "quiz")
    //以下省略
}

 

STEP.3
explanationメソッドを書き換え

71行目あたりのexplanationメソッドを以下のように書き換えてください。

メソッド内の1行目と2行目を追加しました。

func explanation() {
    let correctNumber = Int(quizArray[1])!
    correctLabel.text = "答え:\(quizArray[correctNumber + 2])"

    let answerBtnY = answerBtn1.frame.origin.y
    //以下省略
}

 

STEP.4
実行して確認する

このように、ボタンを押したら、グレーのViewに答えが表示されていたら、成功です。

完了

上記のことを確認できたら、次に進みましょう。

 

戻るボタンを実装する

STEP.1
UIButton()の宣言

28行目あたり(var correctLabel = UILabel()の下)に以下の宣言を記述しましょう

var backBtn = UIButton()

 

STEP.2
ViewDidLoadメソッドを書き換え

30行目あたりのViewDidLoadメソッドを以下のように書き加えてください。

追加した箇所は、真ん中のbackBtnのところのみです。

override func viewDidLoad() {
    super.viewDidLoad()

    //省略
    explanetionBGView.addSubview(correctLabel)

    backBtn.frame = CGRect(x: 90, y: 200, width: 132, height: 37)
    backBtn.backgroundColor = UIColor.white
    backBtn.tintColor = UIColor.white
    backBtn.setTitle("戻る", for: .normal)
    backBtn.setTitleColor(UIColor.black, for: .normal)
    backBtn.addTarget(self, action: #selector(QuizViewController.backBtnTapped), for: UIControl.Event.touchUpInside)
    explanetionBGView.addSubview(backBtn)

    csvArray = loadCSV(fileName: "quiz")
    //以下省略
}
注意
この段階では、エラーが出てしまいます。
STEP.3
backBtnTappedメソッドを追加

78行目あたりに、以下のメソッドを追記します。

これで先ほどのエラーは解消されました。

@objc func backBtnTapped(){
    let screenHeight = Double(UIScreen.main.bounds.size.height)
    UIView.animate(withDuration: 0.5, animations: {() -> Void in self.explanetionBGView.frame = CGRect(x: self.explanationBGX, y: screenHeight, width: 320, height: 210)
    })

    answerBtn1.isEnabled = true
    answerBtn2.isEnabled = true
    answerBtn3.isEnabled = true
    answerBtn4.isEnabled = true
    nextQuiz()
}

 

STEP.4
実行して確認

このように、グレーのViewに、ボタンが出てきたら成功です。

「戻る」を押すと、次の画面に遷移します。

完了

上記のことを確認できたら、次に進みましょう。

 

◯×を表示する

STEP.1
ファイルをダウンロード
STEP.2
zipファイルを解凍

ダウンロードしたzipファイルをダブルクリックして解凍しましょう。

STEP.3
Xcodeのプロジェクトに取り込む

Assets.xcassetsの上にドラッグ&ドロップしましょう。

STEP.4
取り込みの設定

①Copy items if needed、Create groupsにチェック

②「Finish」をクリック

注意

チェックを入れないと大変なことになります。

STEP.5
Main.storyboardを選択

  (show the Project navigator)を選択

②Main.storyboardを選択

STEP.6
imageViewを配置

①プラスボタンをクリックするか、command + shift + Lでオブジェクトウィンドウを表示

②Image Viewをドラッグ&ドロップ

STEP.7
画像の位置大きさを編集

①ImageViewを選択

(Show the Size inspector)を選択

③X、Y、Width、Heightを以下のようにする

X 20
Y 144
Width 374
Height 265
STEP.8
コード画面と2分割

①ImageViewを選択

command + option + control + enterでコード画面と2分割 

STEP.8
紐付け

controlを押しながら、18行目と19行目の間にドラッグ&ドロップ

①「Name」にjudgeImageViewと入力

②「Connect」をクリック

STEP.9
QuizViewControllerを表示

  (show the Project navigator)を選択

QuizViewController.swiftを選択

STEP.10
btnActionメソッドを書き換え

70行目あたりのbtnActionメソッドを以下のように書き換えましょう

@IBAction func btnAction(sender: UIButton) {
    if sender.tag == Int(quizArray[1]) {
        correctCount += 1
        judgeImageView.image = UIImage(named: "correct")
    } else {
        judgeImageView.image = UIImage(named: "incorrect")
    }
    judgeImageView.isHidden = false
    explanation()
}

 

ここでは、先ほど接続したjudgeImageViewに、追加した画像(◯、×)を入れています。

STEP.11
backBtnTappedメソッドを書き換え

backBtnTappedメソッドにjudgeImageView.isHidden = trueを追加しましょう。

@objc func backBtnTapped(){
    let screenHeight = Double(UIScreen.main.bounds.size.height)
    UIView.animate(withDuration: 0.5, animations: {() -> Void in self.explanetionBGView.frame = CGRect(x: self.explanationBGX, y: screenHeight, width: 320, height: 210)
    })

    answerBtn1.isEnabled = true
    answerBtn2.isEnabled = true
    answerBtn3.isEnabled = true
    answerBtn4.isEnabled = true
    judgeImageView.isHidden = true
    nextQuiz()
}

 

STEP.12
実行して確認

正解すると◯が表示され、不正解だと×が表示されます。

戻るボタンを押すと、◯×が消え、次の問題がセットされます。

完了

お疲れ様でした!

これでクイズアプリが完成しました!!

需要があれば、「ランダムに出題」「シェア機能」など追加していきいと思います。

ここまでの全コード

注意

コメントは削除しているので、今後の講座で行がずれてしまう可能性があります。

QuizViewController

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!
    @IBOutlet weak var judgeImageView: UIImageView!
    
    var csvArray: [String] = []
    var quizArray: [String] = []
    var quizCount = 0
    var quizTotal = 5
    var correctCount = 0
    var explanetionBGView = UIView()
    var explanationBGX = 0.0
    var correctLabel = UILabel()
    var backBtn = UIButton()

    override func viewDidLoad() {
        super.viewDidLoad()

        let screenSize:CGSize = UIScreen.main.bounds.size
        explanationBGX = Double(screenSize.width/2) - 320/2
        explanetionBGView.backgroundColor = UIColor.lightGray
        explanetionBGView.frame = CGRect(x: explanationBGX,y:Double(screenSize.height), width: 320, height:  210)
        explanetionBGView.isUserInteractionEnabled = true
        self.view.addSubview(explanetionBGView)

        correctLabel.frame = CGRect(x: 60, y:  100, width: 300, height: 30)
        correctLabel.font = UIFont.systemFont(ofSize: 20)
        explanetionBGView.addSubview(correctLabel)

        backBtn.frame = CGRect(x: 90, y: 200, width: 132, height: 37)
        backBtn.backgroundColor = UIColor.white
        backBtn.tintColor = UIColor.white
        backBtn.setTitle("戻る", for: .normal)
        backBtn.setTitleColor(UIColor.black, for: .normal)
        backBtn.addTarget(self, action: #selector(QuizViewController.backBtnTapped), for: UIControl.Event.touchUpInside)
        explanetionBGView.addSubview(backBtn)

        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)
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        let sVC = segue.destination as! ScoreViewController
        sVC.correct = correctCount
    }

    @IBAction func btnAction(sender: UIButton) {
        if sender.tag == Int(quizArray[1]) {
            correctCount += 1
            judgeImageView.image = UIImage(named: "correct")
        } else {
            judgeImageView.image = UIImage(named: "incorrect")
        }
        judgeImageView.isHidden = false
        explanation()
    }

    @objc func backBtnTapped(){
        let screenHeight = Double(UIScreen.main.bounds.size.height)
        UIView.animate(withDuration: 0.5, animations: {() -> Void in self.explanetionBGView.frame = CGRect(x: self.explanationBGX, y: screenHeight, width: 320, height: 210)
        })

        answerBtn1.isEnabled = true
        answerBtn2.isEnabled = true
        answerBtn3.isEnabled = true
        answerBtn4.isEnabled = true
        judgeImageView.isHidden = true
        nextQuiz()
    }

    func explanation() {
        let correctNumber = Int(quizArray[1])!
        correctLabel.text = "答え:\(quizArray[correctNumber + 2])"

        let answerBtnY = answerBtn1.frame.origin.y
        UIView.animate(withDuration: 0.5, animations: {() -> Void in self.explanetionBGView.frame = CGRect(x: self.explanationBGX, y: Double(answerBtnY), width: 320, height: 280)
        })
        answerBtn1.isEnabled = false
        answerBtn2.isEnabled = false
        answerBtn3.isEnabled = false
        answerBtn4.isEnabled = false
    }

    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: "score", 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
    }
}

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

 

まとめ
  • btn.isEnabled = falseでボタンを非活性
  • Xcodeプロジェクトにファイルをコピーする際は、必ず「Copy items if needed」にチェックを入れる