ちょっと話題の記事

iOSアプリ内に設定画面をかんたんに構築するQuickDialog

2013.06.13

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

QuickDialog!

iOSデバイスを使っている方には馴染み深い「設定」アプリですが、このような設定画面をiOSアプリ内にも持ちたいってことはよくありますよね。しかしいざ作るとなると、実は地味に面倒でそれなりに工数もかかります。そんなときはこのQuickDialogを利用しましょう!

QuickDialogはオープンソース(Apache 2 License)のライブラリです。iOS5以降に対応です。このライブラリを使えば自前でUITableViewを実装してごにょごにょやる必要もなく、とても簡単にアプリ内に設定画面を構築することができます。サンプルにもあるようにデザインのカスタマイズに関しても非常に柔軟に設計されています。また設定画面だけでなく、ログイン画面などのダイアログ画面を素早く構築できます。名前の通りですね。

それでは早速使ってみましょう。

尚、本記事では以下の環境を前提に説明します。

  • Mac OS X 10.8 Moutain lion
  • Xcode 4.6.2
  • iOS SDK 6.1

QuickDialogのデモを見てみよう!

まずは使う前にデモを見てましょう。QuickDialogはGithubで公開されているので、cloneするなりダウンロードするなりしましょう。
escoz/QuickDialog · GitHub

QuickDialogをダウンロードしたら、quickdialog.xcworkspaceをXcodeで開き、QuickDialogExampleスキームを選び実行してみましょう。

ios-quickdialog_1 ios-quickdialog_2

様々なコントロールの利用例や、デザインのカスタマイズサンプルなどを見ることができます。QuickDialogでは、こうしたフォームの作成だけではなく、画面遷移や要素の並べ替えにも対応しています。
それでは早速自分のプロジェクトで使ってみましょう。

サンプルプロジェクトの作成

いつものようにサンプルプロジェクトを作成します。
XcodeよりSingle View Applicationを選択し、以下の内容でプロジェクトを作成しましょう。

項目 設定値
Product Name QuickDialogSample
Organization Name 自分の名前(サンプルなのでテキトー)
Company Identifier 会社名(サンプルなのでテキトー)
Class Prefix なし
Devices iPhone
Use Storyboards チェックする(ストーリーボードを使用)
Use Automatic Reference Counting チェックする(ARC有効)
Include Unit Tests チェックしない(unit testのターゲットを含まない)

QuickDialogのインストール

QuickDialogCocoaPodsを利用するか、ファイルを直接プロジェクトに追加することで利用することができます。

CocoaPodsを使う

CocoaPodsを利用する場合は、Podfileに以下の1行を追加するだけです。

pod 'QuickDialog'

直接プロジェクトに追加する

直接プロジェクトに追加する場合は、まず、QuickDialog/quickdialogディレクトリをプロジェクトに追加します。

次に、作成したプロジェクトのBuild Phasesタブより、Link Binary Librariesを開き、以下のフレームワークを追加します。

  • MapKit.framework
  • CoreLocation.framework

最後にPrefix.pch(本サンプルではQuickDialogSample-Prefix.pch)を以下のように変更すれば準備完了です。

#import <Availability.h>

#ifndef __IPHONE_5_0
#warning "This project uses features only available in iOS SDK 5.0 and later."
#endif

#ifdef __OBJC__
    #import <UIKit/UIKit.h>
    #import <Foundation/Foundation.h>
    #import "QuickDialog.h"
#endif

QuickDialogでHello World!

早速初めてのQuickDialogのソースコードを書いてみましょう。公式ドキュメントを参考にViewController.mを以下のように変更して実行してみましょう。

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
}

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    
    QRootElement *root = [[QRootElement alloc] init];
    root.title = @"Hello World";
    root.grouped = YES;
    QSection *section = [[QSection alloc] init];
    QLabelElement *label = [[QLabelElement alloc] initWithTitle:@"Hello" Value:@"world!"];
    
    [root addSection:section];
    [section addElement:label];
    
    UINavigationController *navigation = [QuickDialogController controllerWithNavigationForRoot:root];
    
    [self presentViewController:navigation animated:YES completion:nil];
}

@end

すると、以下のような画面が表示されるかと思います。

ios-quickdialog_3

このソースコードの登場人物は、

  • QRootElement
  • QSection
  • QLabelElement
  • QuickDialogController

の4つのクラスです。基本的な流れは、

  1. QRootElementインスタンスを生成
  2. QSectionインスタンスを生成してQRootElementインスタンスのaddSection:メソッドで追加
  3. QElement(上記の例ではQLabelElement)を生成してQSectionインスタンスのaddElement:メソッドで追加
  4. QuickDialogControllercontrollerWithNavigationForRoot:で生成したQRootElementインスタンスを指定し、UINavigationControllerインスタンスを生成
  5. 生成したUINavigationControllerインスタンスを表示

になります。これらの代表的なクラスの説明は以下の通りです。

クラス 説明
QRootElement 表示される要素のルートになるクラス。内部的にQSectionの集合を保持する。
QSection UITableViewのセクションに相当するクラスで、QElementの集合を保持する。
QElement UITableViewのセル(UITableViewCell)に相当するクラスで、QuickDialogで用意されている要素はこのクラスのサブクラスとして定義される。このクラスを継承して独自の要素を作ることも可能。
QuickDialogController UITableViewControllerのサブクラスで、実際にダイアログを表示するためのビューコントローラになる。このクラスを継承してカスタマイズすることもできる。

QuickDialogで用意されているパーツ

QuickDialogではデフォルトでたくさんのパーツ(QElementのサブクラス)が用意されています。まずは、以下のソースを実行してみましょう。

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
}

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    
    // ルートエレメントを生成
    QRootElement *root = [[QRootElement alloc] init];
    root.title = @"QuickDialog";
    root.grouped = YES;
    
    // セクションを生成
    QSection *section = [[QSection alloc] init];
    
    // ラベルを生成してセクションに追加
    QLabelElement *labelElement = [[QLabelElement alloc] initWithTitle:@"QLabelElement" Value:@"値"];
    [section addElement:labelElement];
    
    // バッジを生成してセクションに追加
    QBadgeElement *badgeElement = [[QBadgeElement alloc] initWithTitle:@"QBadgeElement" Value:@"値"];
    [section addElement:badgeElement];

    // ON/OFFを生成してセクションに追加
    QBooleanElement *booleanElement = [[QBooleanElement alloc] initWithTitle:@"QBooleanElement" BoolValue:NO];
    [section addElement:booleanElement];

    // ボタンエレメントを生成してセクションに追加
    QButtonElement *buttonElement = [[QButtonElement alloc] initWithTitle:@"QButtonElement"];
    buttonElement.onSelected = ^{
        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@""
                                                            message:@"ボタンが押されたよ!"
                                                           delegate:nil
                                                  cancelButtonTitle:@"OK"
                                                  otherButtonTitles:nil];
        [alertView show];
    };
    [section addElement:buttonElement];
    
    // 日付ピッカーを生成してセクションに追加
    QDateTimeInlineElement *dateTimeElement = [[QDateTimeInlineElement alloc] initWithTitle:@"QDateTimeInlineElement" date:[NSDate date] andMode:UIDatePickerModeDate];
    [section addElement:dateTimeElement];
    
    // テキストフィールドを生成してセクションに追加
    QEntryElement *entryElement = [[QEntryElement alloc] initWithTitle:@"QEntryElement" Value:@"値" Placeholder:@"入力してください"];
    [section addElement:entryElement];
    
    // 数値用テキストフィールドを生成してセクションに追加
    QDecimalElement *decimalElement = [[QDecimalElement alloc] initWithTitle:@"QDecimalElement" value:@123];
    [section addElement:decimalElement];

    // スライダーを生成してセクションに追加
    QFloatElement *floatElement = [[QFloatElement alloc] initWithTitle:@"QFloatElement" value:0.75];
    [section addElement:floatElement];

    // ローディング
    QLoadingElement *loadingElement = [[QLoadingElement alloc] init];
    [section addElement:loadingElement];
    
    // マップを生成してセクションに追加
    QMapElement *mapElement = [[QMapElement alloc] initWithTitle:@"QMapElement" coordinate:CLLocationCoordinate2DMake(35.697239, 139.774719)];
    [section addElement:mapElement];

    // ピッカー
    QPickerElement *pickerElement = [[QPickerElement alloc] initWithTitle:@"QPickerElement" items:@[@[@"いちご", @"みかん", @"ぶどう"]] value:@"いちご"];
    [section addElement:pickerElement];

    // 択一選択を生成してセクションに追加
    QRadioElement *radioElement = [[QRadioElement alloc] initWithItems:@[@"いちご", @"みかん", @"ぶどう"] selected:0 title:@"QRadioElement"];
    [section addElement:radioElement];

    // テキストエリアを生成してセクションに追加
    QTextElement *textElement = [[QTextElement alloc] initWithText:@"QTextElement\nQWebElement\nQWebElement"];
    [section addElement:textElement];

    // テキストエリアを生成してセクションに追加
    QWebElement *webElement = [[QWebElement alloc] initWithTitle:@"QWebElement" url:@"http://classmethod.jp"];
    [section addElement:webElement];
    
    // セクションをルートに追加
    [root addSection:section];
    
    // 複数選択用セクションを生成してルートに追加
    QSelectSection *selectSection = [[QSelectSection alloc] initWithItems:@[@"いちご", @"みかん", @"ぶどう"] selectedIndexes:@[@0, @2] title:@"QSelectSection"];
    [root addSection:selectSection];
    
    // QuickDialogControllerをルートコントローラに設定したUINavigationControllerを生成
    UINavigationController *navigation = [QuickDialogController controllerWithNavigationForRoot:root];
    
    [self presentViewController:navigation animated:YES completion:nil];
}

@end

ios-quickdialog_4

ios-quickdialog_5

これらはすべてQuickDialogで準備されているパーツです。詳細は以下の通りです。

QLabelElement

ios-quickdialog_6

タイトルラベルと値ラベルを表示するだけのシンプルなパーツです。[QLabelElement iamge][QLabelElement imageNamed]プロパティでタイトルラベルの横に画像を表示することもできます。

デモの画像を見ると32x32pxがいい感じのようです。また、QLabelElementは以下のパーツの親クラスとなっているため、これらのパーツでも同様に設定できそうです。

  • QBadgeElement
  • QBooleanElement
  • QButtonElement
  • QEmptyListElement
  • QEntryElement
  • QFloatElement
  • QRadioItemElement
  • QSelectItemElement
  • QWebElement

QBadgeElement

ios-quickdialog_7

機能的にはQLabelElementと変わりませんが、値の表示がバッヂっぽくなっているところが特徴です。

QBooleanElement

ios-quickdialog_8

ON/OFFを切り替えるエレメント。これはよく使いそうですね。[QBooleanElement onImage][QBooleanElement offImage]でON/OFF時の画像を変更することもできます。

QButtonElement

ios-quickdialog_9

名前はボタンですが、単純にセルの真ん中にテキストを配置してボタンぽく見せています。ボタンタップ時のアクションは、他のパーツと同様 [QElement onSelected][QElement controllerAction]プロパティで設定しましょう。

QDateTimeElementとQDateTimeInlineElement

ios-quickdialog_10

ios-quickdialog_11

QDateTimeElementQDateTimeInlineElementはどちらも日付や時間を設定するためのパーツですが、前者が別画面で設定するのに対し、後者は同一画面内で設定するところが違いです。
日付、時間、日付+時間の設定が行えますが、私が確認したところQDateTimeElementでは日付と日付+時間の設定が行えないようです(バグ?)。

QEntryElement

ios-quickdialog_12

入力フィールドを持つエレメントで、プレースホルダーなどUITextFieldの設定から、入力した文字にプレフィックスやサフィックスつけたりもできます。また、入力フィールドのサイズはタイトルの長さに応じて可変となっています。

QDecimalElement

ios-quickdialog_13

数字のみを入力できるQEntryElementのサブクラスです。

QFloatElement

ios-quickdialog_14

スライダーをもつエレメントで、入力フィールド同様、タイトルの長さに応じてサイズが可変になっています。

QLoadingElement

ios-quickdialog_15

ローディング中を示すUIActivityIndicatorViewを表示します。ダイアログの表示項目などを動的に更新する場合などに使いましょう。

QMapElement

ios-quickdialog_16

設定された座標を中心とした地図を別画面で表示することができます。尚、座標の変更などはできないようです。

QPickerElement

ios-quickdialog_17

UIPickerを使用した選択が可能なパーツです。

QRadioElement

ios-quickdialog_18

択一選択を行うパーツです。選択する際は、この要素をタップ後別画面で行います。

QTextElement

ios-quickdialog_19

自由にテキストを表示することのできるパーツです。

QWebElement

ios-quickdialog_20

指定したURL・HTMLを別画面で表示することができます。

QSortingSection

QSortingSectionは要素の並べ替えを行うためのセクションです。ViewController.mを以下のように変更して実行してみましょう。

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
}

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    
    // ルートエレメントを生成
    QRootElement *root = [[QRootElement alloc] init];
    root.title = @"QuickDialog";
    root.grouped = YES;

    // ソートセクションを生成してルートエレメントに追加する
    QSortingSection *sortingSection = [[QSortingSection alloc] init];
    sortingSection.title = @"QSortingSection";
    [sortingSection addElement:[[QLabelElement alloc] initWithTitle:@"いちご" Value:@""]];
    [sortingSection addElement:[[QLabelElement alloc] initWithTitle:@"みかん" Value:@""]];
    [sortingSection addElement:[[QLabelElement alloc] initWithTitle:@"ぶどう" Value:@""]];
    [root addSection:sortingSection];
    
    // QuickDialogControllerをルートコントローラに設定したUINavigationControllerを生成
    UINavigationController *navigation = [QuickDialogController controllerWithNavigationForRoot:root];
    
    [self presentViewController:navigation animated:YES completion:nil];
}

@end

ios-quickdialog_21

まとめ

QuickDialogでは他にもNSDictionaryやJSONから動的にダイアログを生成したり、入力値をまとめて取得したり、デザインを変更するためのプロトコルが用意されていたりとまだまだたくさんの機能があります。
このような画面は実際に実装するとなると難しくはありませんがとても面倒な作業ですよね。そんなときはQuickDialogを使ってみましょう。

参考