iOSアプリ内に設定画面をかんたんに構築するQuickDialog
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スキームを選び実行してみましょう。
様々なコントロールの利用例や、デザインのカスタマイズサンプルなどを見ることができます。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のインストール
QuickDialogはCocoaPodsを利用するか、ファイルを直接プロジェクトに追加することで利用することができます。
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
すると、以下のような画面が表示されるかと思います。
このソースコードの登場人物は、
- QRootElement
- QSection
- QLabelElement
- QuickDialogController
の4つのクラスです。基本的な流れは、
- QRootElementインスタンスを生成
- QSectionインスタンスを生成してQRootElementインスタンスのaddSection:メソッドで追加
- QElement(上記の例ではQLabelElement)を生成してQSectionインスタンスのaddElement:メソッドで追加
- QuickDialogControllerのcontrollerWithNavigationForRoot:で生成したQRootElementインスタンスを指定し、UINavigationControllerインスタンスを生成
- 生成した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
これらはすべてQuickDialogで準備されているパーツです。詳細は以下の通りです。
QLabelElement
タイトルラベルと値ラベルを表示するだけのシンプルなパーツです。[QLabelElement iamge]か[QLabelElement imageNamed]プロパティでタイトルラベルの横に画像を表示することもできます。
デモの画像を見ると32x32pxがいい感じのようです。また、QLabelElementは以下のパーツの親クラスとなっているため、これらのパーツでも同様に設定できそうです。
- QBadgeElement
- QBooleanElement
- QButtonElement
- QEmptyListElement
- QEntryElement
- QFloatElement
- QRadioItemElement
- QSelectItemElement
- QWebElement
QBadgeElement
機能的にはQLabelElementと変わりませんが、値の表示がバッヂっぽくなっているところが特徴です。
QBooleanElement
ON/OFFを切り替えるエレメント。これはよく使いそうですね。[QBooleanElement onImage]と[QBooleanElement offImage]でON/OFF時の画像を変更することもできます。
QButtonElement
名前はボタンですが、単純にセルの真ん中にテキストを配置してボタンぽく見せています。ボタンタップ時のアクションは、他のパーツと同様 [QElement onSelected]や[QElement controllerAction]プロパティで設定しましょう。
QDateTimeElementとQDateTimeInlineElement
QDateTimeElementとQDateTimeInlineElementはどちらも日付や時間を設定するためのパーツですが、前者が別画面で設定するのに対し、後者は同一画面内で設定するところが違いです。
日付、時間、日付+時間の設定が行えますが、私が確認したところQDateTimeElementでは日付と日付+時間の設定が行えないようです(バグ?)。
QEntryElement
入力フィールドを持つエレメントで、プレースホルダーなどUITextFieldの設定から、入力した文字にプレフィックスやサフィックスつけたりもできます。また、入力フィールドのサイズはタイトルの長さに応じて可変となっています。
QDecimalElement
数字のみを入力できるQEntryElementのサブクラスです。
QFloatElement
スライダーをもつエレメントで、入力フィールド同様、タイトルの長さに応じてサイズが可変になっています。
QLoadingElement
ローディング中を示すUIActivityIndicatorViewを表示します。ダイアログの表示項目などを動的に更新する場合などに使いましょう。
QMapElement
設定された座標を中心とした地図を別画面で表示することができます。尚、座標の変更などはできないようです。
QPickerElement
UIPickerを使用した選択が可能なパーツです。
QRadioElement
択一選択を行うパーツです。選択する際は、この要素をタップ後別画面で行います。
QTextElement
自由にテキストを表示することのできるパーツです。
QWebElement
指定した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
まとめ
QuickDialogでは他にもNSDictionaryやJSONから動的にダイアログを生成したり、入力値をまとめて取得したり、デザインを変更するためのプロトコルが用意されていたりとまだまだたくさんの機能があります。
このような画面は実際に実装するとなると難しくはありませんがとても面倒な作業ですよね。そんなときはQuickDialogを使ってみましょう。