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

サトリク

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

 

クイズアプリPert6の作業の流れ

今の状態

サトリク

今の状況としては、

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

このPartでは、正解の場合は◯を表示し、不正解の場合は×を表示する機能をつけていきたいと思います!

 

 

◯×を表示させる

まずは、選択肢を押したら、正解の場合は◯を不正解の場合は×を表示させてみましょう。

STEP.1
ファイルをダウンロード

以下のボタンを押してダウンロードしてください。

◯と×の画像データが入っています。

画像ダウンロード

STEP.2
ファイルを解凍

Finderのダウンロードを開いて、先程ダウンロードしたimage.zipをダブルクリックで解凍しましょう。

STEP.3
画像確認

以下のように、◯と×の画像が入っていることを確認しましょう。

STEP.4
プロジェクトにインポート

プロジェクトにインポートしましょう。

画像のように、imageフォルダをプロジェクト(SampleQuizフォルダの中)にドラッグ&ドロップしてください。

STEP.5
取り込みの設定

以下の画像のように設定してください。

Copy items if neededにチェック

Create groupsにチェック

SampleQuizにチェック

④「Finish」をクリック

注意

Copy items if neededにチェックを入れないややめんどくさいことになります。

STEP.6
Main.storyboardを選択

左のメニューからMain.storyboardを選択してください。

STEP.7
ImageViewを配置

問題画面にImageViewを配置しましょう。

①右上のプラスボタンをクリック

②ImageViewを問題画面にドラッグ&ドロップ

STEP.8
Imageの位置と大きさを変更

①ImageViewを選択

②  (Show the Size inspector)を選択

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

X 20
Y 100
Width 374
Height 374
STEP.9
コードと2分割

①問題画面のバーをクリック

command + option + control + enterで2分割表示

STEP.10
コードと紐づけ

controlを押しながら、ドラッグ&ドロップ

 

NameにjudgeImageViewと入力し、Connect

STEP.11
QuizViewController.swiftを選択

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

STEP.12
◯×を表示させるコードを追記

ボタンを押した時に呼ばれるブロック内で、正解だったら紐付けたjudgeImageViewに◯の画像を代入し、不正解だったら×の画像を代入するコードを追記しましょう。

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

STEP.13
実行して確認

このように選択肢を押したら、正解だったら◯不正解だったら×が表示されるはずです。

完了

これで◯×の表示ができました。

しかし、◯×がずっと表示されたままです。0.5秒経ったら非表示にする処理を追記しましょう。

 

◯×を0.5秒後に非表示にする

次は、◯×を0.5秒経ったら非表示にする処理を付け加えていきましょう。

STEP.1
0.5秒後に非表示にする

〇〇秒後に処理をしたい場合は、以下のように書きます。

この場合だと、judgeImageView(◯×)のisHidden(隠すかどうか)をtrue(隠す)にしている処理を0.5秒後におこないます。

DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
    self.judgeImageView.isHidden = true
}

STEP.2
実行して確認

 

このように、ボタンを押したら非表示になりました。

が、しかし2問目の選択肢を押しても◯×が出なくなりました。

STEP.3
2問目以降も◯×を表示するようにする

2問目以降表示されないのは、judgeImageView.isHidden = trueにしたせいです。要は◯×の画像を隠している状態だからです。

なので、ボタンを押したら、◯×の画像を再表示する処理を追記してあげましょう。

@IBAction func btnAction(sender: UIButton) {
    if sender.tag == Int(quizArray[1]) {
        correctCount += 1
        print("正解")
        judgeImageView.image = UIImage(named: "correct")
    } else {
        print("不正解")
        judgeImageView.image = UIImage(named: "incorrect")
    }
    print("スコア:\(correctCount)")
    judgeImageView.isHidden = false
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
        self.judgeImageView.isHidden = true
    }
    nextQuiz()
}

STEP.4
実行して確認

そうするとこのように、2問目以降の問題でも◯×が表示されると思います。

完了

これで、◯×を0.5秒後に非表示する処理ができました。

 

選択肢のダブルタップを防ぐ

少し説明が難しいのですが、現状、選択肢をトントンとダブルタップすると、問題文や選択肢が表示する前に次の問題が解かれてしまいます。

なので以下の対応をします。

  1. ◯×が非表示になってから、次の問題をセットする
  2. 選択肢を押してから◯×が非表示になるまでボタンを押せなくする
STEP.1
◯×が非表示になってから次の問題をセットする

この対応は簡単です。

次の問題をセットするブロックnextQuiz()を0.5秒後に表示させるブロックに移動させるだけです。

で、0.5秒後に表示させるブロック内ではself.を付けないといけないので、以下のようにselfをつけて記述してください。

@IBAction func btnAction(sender: UIButton) {
    if sender.tag == Int(quizArray[1]) {
        correctCount += 1
        print("正解")
        judgeImageView.image = UIImage(named: "correct")
    } else {
        print("不正解")
        judgeImageView.image = UIImage(named: "incorrect")
    }
    print("スコア:\(correctCount)")
    judgeImageView.isHidden = false
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
        self.judgeImageView.isHidden = true
        self.nextQuiz()
    }
}

STEP.2
実行して確認

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

おそらく、◯×が非表示になった瞬間に次の問題がセットされるようになっているはずです。

STEP.3
◯×が非表示になるまでボタンを押せなくする

ボタンを押したら、ボタンを押せなくしましょう。

押せなくするには、answerButton1isEnabled(有効かどうか)をfalse(無効)にします。

@IBAction func btnAction(sender: UIButton) {
    if sender.tag == Int(quizArray[1]) {
        correctCount += 1
        print("正解")
        judgeImageView.image = UIImage(named: "correct")
    } else {
        print("不正解")
        judgeImageView.image = UIImage(named: "incorrect")
    }
    print("スコア:\(correctCount)")
    judgeImageView.isHidden = false
    answerButton1.isEnabled = false
    answerButton2.isEnabled = false
    answerButton3.isEnabled = false
    answerButton4.isEnabled = false
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
        self.judgeImageView.isHidden = true
        self.nextQuiz()
    }
}

注意

ImageViewのisHiddenと反対の動きをするので、isHiddenisEnabledの違いに注意してください。

コード 意味 true時 false時
isHidden 隠すかどうか 隠す 隠さない
isEnabled 有効にするかどうか 有効にする 無効にする
STEP.4
実行して確認

実行して選択肢ボタンを押すとこのようにボタンが機能しなくなります。

このままでは、2問目の問題が解けないので、◯×が非表示になったらまたボタンが機能するようにしましょう。

STEP.5
◯×が非表示になったらまたボタンが機能するようにする

やり方は簡単で、0.5秒経ったら有効にすればいいだけです。

0.5秒経ったら実行されるブロックに以下のように記述してください。

@IBAction func btnAction(sender: UIButton) {
    if sender.tag == Int(quizArray[1]) {
        correctCount += 1
        print("正解")
        judgeImageView.image = UIImage(named: "correct")
    } else {
        print("不正解")
        judgeImageView.image = UIImage(named: "incorrect")
    }
    print("スコア:\(correctCount)")
    judgeImageView.isHidden = false
    answerButton1.isEnabled = false
    answerButton2.isEnabled = false
    answerButton3.isEnabled = false
    answerButton4.isEnabled = false
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
        self.judgeImageView.isHidden = true
        self.answerButton1.isEnabled = true
        self.answerButton2.isEnabled = true
        self.answerButton3.isEnabled = true
        self.answerButton4.isEnabled = true
        self.nextQuiz()
    }
}

STEP.6
実行して確認

これで2回目以降も問題が解けるようになります。

完了

これでpart6の◯×を表示する処理は完成です!

 

ここまでの全コード

QuizViewController.swift

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!
    @IBOutlet var judgeImageView: UIImageView!
    
    var csvArray: [String] = []
    var quizArray: [String] = []
    var quizCount = 0
    var correctCount = 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.
    }
    
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        let scoreVC = segue.destination as! ScoreViewController
        scoreVC.correct = correctCount
    }
    
    //ボタンを押したときに呼ばれる
    @IBAction func btnAction(sender: UIButton) {
        if sender.tag == Int(quizArray[1]) {
            correctCount += 1
            print("正解")
            judgeImageView.image = UIImage(named: "correct")
        } else {
            print("不正解")
            judgeImageView.image = UIImage(named: "incorrect")
        }
        print("スコア:\(correctCount)")
        judgeImageView.isHidden = false
        answerButton1.isEnabled = false
        answerButton2.isEnabled = false
        answerButton3.isEnabled = false
        answerButton4.isEnabled = false
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
            self.judgeImageView.isHidden = true
            self.answerButton1.isEnabled = true
            self.answerButton2.isEnabled = true
            self.answerButton3.isEnabled = true
            self.answerButton4.isEnabled = true
            self.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.
     }
     */
    
}

 

この後のクイズアプリ講座について

サトリク

お疲れ様でした!ここまでうまくできましたか?

初心者の方が、アプリ開発の知識を身につけるには、まず、リリースしてみることです。

初心者の方でまだ何もアプリをリリースしたことがない人は、このクイズアプリをリリースしましょう。当然このままだとリリースできないので、もう少し機能を付け足しましょう。

この先の講座では、よりリリースできるようなアプリを目指して作っていきます。

Part7以降にやること
  • ジャンル選択機能
  • ランダムで出題機能
  • シェア機能
  • レイアウトをよくする
  • AutoLayoutをつける
  • バナー広告をつける
  • 起動画面の作成
  • リリース手順

サトリク

Part7以降では、zennというサイトで有料で行っていきます。

次の講座(Pert7)

作成中です。今月中(2021/1)には作成致します。

 

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

RikutoSato

自分一人でリリースできるか不安という方に向けて、このブログの筆者でもある自分が、MENTAというサービスを使って月額4000円で皆さんのクイズアプリをAppStoreにリリースするまで、全力でサポートします。

以下の内容でやっています。

  • Part7まで無料公開します。←当たり前ですが。。
  • AppStoreにリリースできるまでサポートします。
  • あなたのやりたい機能の実装をサポートします。
  • 不明点をなど質問し放題です。(24時間以内に返信実際は(一瞬で返します))
  • 理解不能だったらビデオチャットで画面を映して解説します。
  • Slack、アプリ開発グループに招待します。

気軽にご相談ください!

詳細はこちら

 

このクイズアプリ講座で、実際にリリースまでできたアプリ

Kさん:Generation Quiz

10月13日〜12月8日と、大体2ヶ月でリリースできました!

BGMや、時間制限、スコア保存、広告など、いろいろな機能を追加しました。

Generation Quiz
Generation Quiz

 

サトリク

この続きは、Zennか、MENTAで!