【かけ出しiPhoneアプリ開発】クイズアプリ



【かけ出しiPhoneアプリ開発】クイズアプリ

iPhoneアプリ開発をはじめたばかりの方にお届けする 【かけ出しiPhoneアプリ開発】 シリーズ。
今回は 『クイズアプリ』 の作成方法をご紹介。

クイズをベースに「テスト問題アプリ」や「資格用アプリ」に内容を変えると、付加価値が産めそうですね。

本稿で使用する XCode のバージョン: XCode 10.1、 Swift 4.2

目次
  1. 【かけ出しiPhoneアプリ開発】クイズアプリ
  2. 【今回の目標】クイズ・プログラムの基本を理解
  3. クイズアプリの作成(ストーリーボード)
  4. クイズアプリの作成(プログラミング)
  5. クイズ問題に画像を追加
  6. まとめ

【かけ出しiPhoneアプリ開発】クイズアプリ

【今回の目標】クイズ・プログラムの基本を理解

image

予め用意した問題をランダムに表示させて、 3つの選択肢から回答。そして答え合わせをして、結果を同一ページ内に表示。Nextボタンで次の問題をランダムに表示。

プログラミング初心者にとって、クイズアプリの処理工程は参考になると思います。基本を理解して、応用できるようにがんばりましょう。

クイズアプリのサンプル

クイズアプリの作成(ストーリーボード)

Step.1 新規プロジェクトの作成

image

XCode起動、 「Create a New Xcode project」、 「Single View App」 そして プロジェクト名を入力し Create。今回はデバイスの回転によるレイアウト変更を行わないように、初期設定画面の 「Deployment Info」内にある 「Landscape Left」 と 「Landscape Right」 のチェックを外しておきます。

Step.2 ストーリーボードのサイズ調整

image

今回作成するクイズアプリの画面上には、 UI部品の Label が 2つ、 Button が 3つ搭載される予定です。複数の部品を上手く表示しようと思うと、ベースとなる画面サイズも重要。今回は画面サイズの小さい iPhone SE 基準でレイアウトしていきます。

尚、デフォルトの状態では、ストーリーボードは iPhone8 になっているでしょう。機種変更する方法は、ストーリーボード下の 「Device」ボタンから。

Step.3 UI部品の配置

【UI部品の配置 完成イメージ】

image

それではクイズアプリに必要な部品群を配置していきましょう。上図完成イメージを元にまずは Label から。

image

Labelがセット出来たら、 Button を 3つ、 それから回答結果用のラベルを 1つと Nextボタン用のボタンを 1つ配置します。

Step.4 UI部品のレイアウト調整

image

今回はシンプルにすべての UI部品を横方向中央にし、縦方向については 1つのボタンを基準に設定していきます。こうすることで iPad でアプリを開いた時も部品の間隔を保つことができ、使いやすいでしょう。

まずはストーリーボード上のすべての部品を左クリックしたままでドラッグします。そして全部の部品が選択された状態で、「Horizontally Container」 にチェック。

image

今回は 3つめのボタンを基準にすべく(上図参照)、 3つ目のボタンの縦方向も中央表示するように 「Vertically in Container」 にチェック。

image

次は基準ボタンより上にある ボタン2つ と ラベル1つ をドラッグして選択。そして 3つの部品が選択された状態で、 |-□-| をクリック。下方向の余白を 10 にセット。これで 3つの部品全てに下方向の余白が 10 とセットされます。

image

先ほど同様に 基準ボタンより下の 2つの UI部品も、上方向の余白が 10 になるよう設定。

それで各ラベルやボタンの表示内容を変更して、レイアウトを確認してみます。

image

レイアウトのバランスとしては大丈夫でしょう。

クイズ・アプリらしくボタンをカラーリングしたり、背景を変更したりなど、デザイン要素を加えると見た目がGood。ただし、今回は工数の関係から割愛させて頂きます。

Step.5 UI部品の接続(その1)

image

画面上にある UI部品を ViewController.swift に接続していきます(上図参照)。まずは各部品をプロパティとして接続。接続時の詳細設定は以下の通り。

【ラベル】

image

Connection: Outlet
Object: View Controller
Name: 任意(今回は questionLabel)
Type: UIbutton
Storage: Weak

【ボタン】

image

Connection: Outlet
Object: View Controller
Name: 任意(今回は button1 、 button2 etc)
Type: UIbutton
Storage: Weak

image

接続結果は上記のようになります。

Step.6 UI部品の接続(その2)

image

今度は、回答ボタンが押された時の振る舞いを制御するために、 回答ボタン 3つを Action(メソッド) として接続。

Connection: Action
Object: View Controller
Name: 任意(今回は button1Action etc)
Type: Any
Event: Touch Up Inside
Arguments: Sender

image

回答ボタン 3つと Nextボタン、計4つを Action として接続。

ここで一旦シミュレーターを起動してみますと、以下のように。

image

現状は部品をつないだだけで何のプログラム処理も加えてませんので、ボタンを押しても何も起きません。

クイズアプリの作成(プログラミング)

Step.7 クイズの出力

image

まずは問題を表示するためのプログラムを考えます。問題文は questionLabel.text というプロパティに文章を代入し、 回答文は button.setTitle() を使ってボタン内に文字を表示。

questionLabel.text = "1+1="
button1.setTitle("5", for:UIControl.State.normal)
button2.setTitle("2", for:UIControl.State.normal)
button3.setTitle("1", for:UIControl.State.normal)

最終行にこの問題の答えをメモしておきましょう。

collectAnswer ="2"

collectAnswer は変数で、

var collectAnswer = String()

など事前に設定しておきましょう。 また collectAnswer は、回答ボタンと連動します。

image

Step.8 答え合わせ

image

Step.7 の問題設定時に回答を collectAnswer に記録しましたね。アプリ使用時、クイズが表示されて、ユーザーは回答ボタンを押します。その 回答ボタン = collectAnswer であれば正解、ということですね。

上図でもコードを表示していますが、以下のような感じでしょう。

if (collectAnswer == "2"){
    // 正解
}
else{
    // 間違い
}

collectAnswer が 2 だった時は正解、それ以外は間違い、という処理工程。回答結果は、ラベルの endLabel に表示。

Step.9 問題をランダムに表示

image

今回はクイズ 3問をランダムに表示。ランダム処理には

arc4random()

という関数が便利。 arc4random() を使って問題をランダム処理するために、一つ func randomQuestion(){} というファンクションを設けます。

そして各問題を switch文の中に格納し、コンテンツ制御。

Step.10 次の問題を表示

@IBAction func Next(_ sender: Any) {
    randomQuestion()
}

次の問題が表示されるようにするには、 Nextボタンに ランダム表示の randomQuestion() を搭載すれば OK。上記コードで Next ボタンを押すと次の問題がランダムに表示されます。

Step.11 クイズの基本プログラム

Step.7 から Step.10 を元にランダムに問題が表示され、答え合わせのできるプログラムを作成した結果、以下のようなコードになりました。

import UIKit
//
class ViewController: UIViewController {
    //
    // propaty ////////////////////////
    @IBOutlet weak var questionLabel: UILabel!
    //
    @IBOutlet weak var button1: UIButton!
    @IBOutlet weak var button2: UIButton!
    @IBOutlet weak var button3: UIButton!
    //
    @IBOutlet weak var endLabel: UILabel!
    @IBOutlet weak var nextButton: UIButton!
    //
    var collectAnswer = String()
    //
    override func viewDidLoad() {
        super.viewDidLoad()
        randomQuestion()
    }
    //
    // function//////////////////////
    // 問題をランダム表示するためのプログラム
    func randomQuestion(){
        var randomNumber = arc4random() % 3
        randomNumber += 1
        //
        switch(randomNumber){
        case 1:
            questionLabel.text = "1+1="
            img.image = imgAnt
            button1.setTitle("3", for: UIControl.State.normal)
            button2.setTitle("2", for: UIControl.State.normal)
            button3.setTitle("1", for: UIControl.State.normal)
            collectAnswer = "2"
            break
        case 2:
            questionLabel.text = "4-1="
            button1.setTitle("3", for: UIControl.State.normal)
            button2.setTitle("2", for: UIControl.State.normal)
            button3.setTitle("1", for: UIControl.State.normal)
            collectAnswer = "1"
            break
        case 3:
            questionLabel.text = "3-2="
            button1.setTitle("3", for: UIControl.State.normal)
            button2.setTitle("2", for: UIControl.State.normal)
            button3.setTitle("1", for: UIControl.State.normal)
            collectAnswer = "3"
            break
        default:
            break
        }
    }
    //
    // Button Action /////////////////////////////
    @IBAction func button1Action(_ sender: Any) {
        if (collectAnswer == "1"){
            endLabel.text = "OK"
        }
        else{
            endLabel.text = "buuuu!"
        }
    }
    @IBAction func button2Action(_ sender: Any) {
        if (collectAnswer == "2"){
            endLabel.text = "OK"
        }
        else{
            endLabel.text = "buuuu!"
        }
    }
    @IBAction func button3Action(_ sender: Any) {
        if (collectAnswer == "3"){
            endLabel.text = "OK"
        }
        else{
            endLabel.text = "buuuu!"
        }
    }
    @IBAction func Next(_ sender: Any) {
        randomQuestion()
    }    
}

【実行結果】

image

ランダムに問題が表示されて、答え合わせもできるのでいいのですが、次の問題にいった時も前の回答が残って表示され、見難いです。この問題は isHidden で解決できます。

Step.12 答え合わせの表示・非表示制御

image

クイズ回答後、次の問題にいった時、前の回答結果を非表示するためには isHidden を使うと便利。まずは表示・非表示の機能を追加します。

// 表示
func hide(){
}

// 非表示
func unHide(){
}

そしてこの関数内に表示・非表示させたい対象のプロパティを記載。

func hide(){
    endLabel.isHidden = true
    nextButton.isHidden = true
}
func unHide(){
    endLabel.isHidden = false
    nextButton.isHidden = false
}

あとは、表示させたい時、非表示させたい時のタイミングに hide() 、 unHide() を追記すればOK。例えば、アプリ起動時は回答結果を非表示にしときたいですよね。コード上ではこんな風になります。

override func viewDidLoad() {
    super.viewDidLoad()
    hide()
    randomQuestion()
}


【非表示機能も搭載したコード全文】

import UIKit
//
class ViewController: UIViewController {
    //
    // propaty ////////////////////////
    @IBOutlet weak var questionLabel: UILabel!
    //
    @IBOutlet weak var button1: UIButton!
    @IBOutlet weak var button2: UIButton!
    @IBOutlet weak var button3: UIButton!
    //
    @IBOutlet weak var endLabel: UILabel!
    @IBOutlet weak var nextButton: UIButton!
    //
    var collectAnswer = String()
    //
    override func viewDidLoad() {
        super.viewDidLoad()
        hide()
        randomQuestion()
    }
    //
    // function//////////////////////
    // 問題をランダム表示するためのプログラム
    func randomQuestion(){
        var randomNumber = arc4random() % 3
        randomNumber += 1
        //
        switch(randomNumber){
        case 1:
            questionLabel.text = "1+1="
            img.image = imgAnt
            button1.setTitle("3", for: UIControl.State.normal)
            button2.setTitle("2", for: UIControl.State.normal)
            button3.setTitle("1", for: UIControl.State.normal)
            collectAnswer = "2"
            break
        case 2:
            questionLabel.text = "4-1="
            button1.setTitle("3", for: UIControl.State.normal)
            button2.setTitle("2", for: UIControl.State.normal)
            button3.setTitle("1", for: UIControl.State.normal)
            collectAnswer = "1"
            break
        case 3:
            questionLabel.text = "3-2="
            button1.setTitle("3", for: UIControl.State.normal)
            button2.setTitle("2", for: UIControl.State.normal)
            button3.setTitle("1", for: UIControl.State.normal)
            collectAnswer = "3"
            break
        default:
            break
        }
    }
    //
    func hide(){
        endLabel.isHidden = true
        nextButton.isHidden = true
    }
    func unHide(){
        endLabel.isHidden = false
        nextButton.isHidden = false
    }
    // Button Action /////////////////////////////
    @IBAction func button1Action(_ sender: Any) {
        unHide()
        if (collectAnswer == "1"){
            endLabel.text = "OK"
        }
        else{
            endLabel.text = "buuuu!"
        }
    }
    @IBAction func button2Action(_ sender: Any) {
        unHide()
        if (collectAnswer == "2"){
            endLabel.text = "OK"
        }
        else{
            endLabel.text = "buuuu!"
        }
    }
    @IBAction func button3Action(_ sender: Any) {
        unHide()
        if (collectAnswer == "3"){
            endLabel.text = "OK"
        }
        else{
            endLabel.text = "buuuu!"
        }
    }
    @IBAction func Next(_ sender: Any) {
        randomQuestion()
        hide()
    }
}

【実行結果】

image

クイズ問題に画像を追加

image

せっかくなので実用的なクイズ・アプリにしようと思い、子供向け教材として 「身近に潜む危険な昆虫を知る」 アプリにしてみました。

Step.1 レイアウトの解除

『クイズアプリの作成』で作った XCode をベースとして、問題の表示時に画像を追加してみます。レイアウトのやり直しやストーリーボードを使わない画像の取り扱いなどが学べると思います。

image

まずレイアウトのやり直しについては、ストーリーボード上の UI部品をすべて選択し、 |-△-| ボタンをクリック後、 「Clear Constraints」 を選択。すると今まで設定してきたレイアウトが解除されます。

Step.2 レイアウトの基準設定

image

今まで問題文が表示されていたところに画像を表示しますので、部品全体を全部下げる必要があります。まずはレイアウトの基準を 回答 1ボタン として作業開始。回答ボタン1 の縦横方向を中央にセット。そして他の部品を横方向中央、縦方向 10 のスペースとなるようにレイアウト編集。以下のように画面上部にスペースを設けることができました。(Step.4 UI部品のレイアウト調整 参照)

image

Step.3 画像表示の用意(UI部品)

image

画像を表示させるスペースに UI部品の Image View をセット。

image

今回余白は上記のようにしました。画像の場合、横方向 中央を設定すると黄色エラーで「曖昧な設定」と判断されます。そのため画面枠との余白スペースでレイアウトを調整。

Step.4 画面表示の用意(接続)

image

問題毎に表示される画像を変えようと思うと、Swiftコードで画像を操作する必要があります。例えば以下のようなコードで。

ImageView部品接続時のプロパティ.image = 画像プロパティ

このコードを満たすためには、まず UI部品 「Image View」 を プロパティ(Outlet) として接続する必要が。接続時の詳細設定は以下の通り。

image

Step.4 画面表示の用意(画像プロパティ)

image

Swiftコードで画像を扱うには、以下のようなコードが必要。

ImageView部品接続時のプロパティ.image = 画像プロパティ

あとは画像プロパティの設定があれば OK ですね。コードが開いているついでに、今回表示する画像のプロパティも入力しておきましょう。

var imgAnt = UIImage(named: "ari")
var imgSpider = UIImage(named: "kumo")
var imgCaterpillar = UIImage(named: "kemushi")

これで Swiftコードで画像を操作でき、問題毎に画像を切り替えられます。肝心の画像データが XCode にセットされていませんので、画像をアップしましょう。

Step.5 画面表示の用意(画像のアップロード)

image

XCodeへの画像アップは、XCode左サイドバー内にある Assets.xcassets(青色) を左クリックし、パソコン内の画像ファイルを XCode 画面中央の空きスペースにドラッグ&ドロップ、で OK ですね。

Step.6 プログラミング

image

Swiftコードで画像を表示するには以下のようなコードが必要でしたね。

ImageView部品接続時のプロパティ.image = 画像プロパティ

Step.4 と 5 で設定した内容をもとにコードを書くと

img.image = imgAnt

といった感じに。

尚、今回は画像表示に関する細かい設定は割愛させて頂きました。

【実行結果】

image


【非表示機能も搭載したコード全文】

import UIKit
//
class ViewController: UIViewController {
    // プロパティ
    @IBOutlet weak var questionLabel: UILabel!
    @IBOutlet weak var button1: UIButton!
    @IBOutlet weak var button2: UIButton!
    @IBOutlet weak var button3: UIButton!
    @IBOutlet weak var endLabel: UILabel!
    @IBOutlet weak var Next: UIButton!
    //
    @IBOutlet weak var img: UIImageView!
    //
    var imgAnt = UIImage(named: "ari")
    var imgSpider = UIImage(named: "kumo")
    var imgCaterpillar = UIImage(named: "kemushi")
    //
    var collectAnswer = String()
    //
    override func viewDidLoad() {
        super.viewDidLoad()
        hide()
        randomQuestion()
    }
    //
    func randomQuestion(){
        var randomNumber = arc4random() % 3
        randomNumber += 1
        //
        switch(randomNumber){
        case 1:
            questionLabel.text = "このアリはヤバイ?"
            img.image = imgAnt
            button1.setTitle("ヤバくない", for: UIControl.State.normal)
            button2.setTitle("どうだろう", for: UIControl.State.normal)
            button3.setTitle("危険", for: UIControl.State.normal)
            collectAnswer = "3"
            break
        case 2:
            questionLabel.text = "このクモは危険かな?"
            img.image = imgSpider
            button1.setTitle("危険じゃない", for: UIControl.State.normal)
            button2.setTitle("クモは全部危険", for: UIControl.State.normal)
            button3.setTitle("危険", for: UIControl.State.normal)
            collectAnswer = "3"
            break
        case 3:
            questionLabel.text = "この毛虫、触ったけど大丈夫?"
            img.image = imgCaterpillar
            button1.setTitle("大丈夫", for: UIControl.State.normal)
            button2.setTitle("絶対ヤバイ", for: UIControl.State.normal)
            button3.setTitle("親に聞くわ", for: UIControl.State.normal)
            collectAnswer = "1"
            break
        default:
            break
        }
    }
    //
    func hide(){
        endLabel.isHidden = true
        Next.isHidden = true
    }
    func unHide(){
        endLabel.isHidden = false
        Next.isHidden = false
    }
    //
    @IBAction func button1Action(_ sender: Any) {
        unHide()
        if (collectAnswer == "1"){
            endLabel.text = "OK"
        }
        else{
            endLabel.text = "buuuu!"
        }
    }
    @IBAction func button2Action(_ sender: Any) {
        unHide()
        if (collectAnswer == "2"){
            endLabel.text = "OK"
        }
        else{
            endLabel.text = "buuuu!"
        }
    }
    @IBAction func button3Action(_ sender: Any) {
        unHide()
        if (collectAnswer == "3"){
            endLabel.text = "OK"
        }
        else{
            endLabel.text = "buuuu!"
        }
    }
    @IBAction func Next(_ sender: Any) {
        hide()
        randomQuestion()
    }
}

最新のアプリ開発スキルが身に付く

CodeCampの無料体験はこちら

まとめ

今回はクイズアプリの作成を通じて、「表示・非表示 機能」それから「画像をコードで制御」 というテクニックも学びました。この他にも点数機能やメニュー画面などを設けるとより本格的なクイズ・アプリになりそうですね。

「チュートリアル系のアプリを開発したい」 「ランキング機能をつけたクイズアプリを作りたい」 と考えている方、 Swift の基礎はお済みでしょうか?

Swift の学習方法はいくつかありますが、スピードと正確性を求めるなら「プログラミング・スクール」でしょう。中でも「オンライン形式」のプログラミング・スクールは 「時間」 と 「場所」 の制約を受けにくいことから効率的。

オンラインのプログラミング・スクールで定評のある CodeCamp では、オンライン・レッスンをよく知らない方向けに 無料体験・無料相談 を実施中。オンラインの学習環境を体験したことがない方、一度お試しはいかがでしょうか?

関連記事

オシママサラ
この記事を書いた人
オシママサラ
\ 無料体験開催中!/自分のペースで確実に習得!
オンライン・プログラミングレッスンNo.1のCodeCamp