hyoromoのブログ

最近はVRSNS向けに作ったものについて書いています

FirefoxOSについてなんとなく調べてみた


HTML5で書けるプラットフォームだよ、お兄ちゃん☆」て印象しかなくKEON端末を買ってしまったので、今回調べてみたのでその事をサラッと書きます。

概要

HTML5Mozilla製独自APIを利用することでアプリ開発が出来るWebOS。

Mozillaはどうしたいの?

公式サイトを引用すると以下の様なフレーズが目につきます。

独占的モバイルプラットフォームからの解放
開発者のための機会
消費者の自由

HTML5+Mozilla製独自APIだから開発楽ちんだよ、お兄ちゃん☆特定のプラットフォームに特化しなくてもWebサイト作るノリでアプリつくっちゃいなよ。的な感じなのでしょう。
コア処理をHTML5で書けるようなのでFirefoxOS向けだけではなく、他WebOSやWebサイト向けにも流用できる為、それによりユーザが欲しいアプリも開発者が提供してくれやすいのでわ。という事なのでしょう。

開発者にとっては?

HTML5で書けるので他プレットフォームへ提供するついでにアプリが実装できる「かも」。
Mozilla製独自APIの学習は必要だが、それ以外の学習コストは必要ないのでWebエンジニアが保有する知識の延長線上で開発可能「かも」。逆にネイティブエンジニアにとってはHTML5等の学習コストは発生する。
Firefoxブラウザからシミュレータ起動出来るから環境を整えるコストは低い。

消費者にとっては?

Webサイトへアクセスしているのと変わらない感覚でアプリを利用出来るので、画面操作を覚える必要がほぼない「かも」。
アプリをインストールしていなくても、ホーム画面から目的に応じたアプリを起動(単にブラウザがURL先を表示してるだけ)してくれ、アプリやWebサイトを気にすること無く目的に応じた事をしやすい「といいね」。

KEONは?

DEVELOPER PREVIEW PHONEの2種類のうちローエンドモデルなのでスペックはショボイ。そのせいかプリインされているFirefoxブラウザアプリを筆頭に全体的にモッサリしている&リンク先をタップしても反応しない事が多々。ここは出たばかりのプラットフォームだからというのもあると思うけど、過度な期待をせずAndroid1.5時代の端末を触ってると思えば印象も変わってくるでしょう。実際Android1.5にActionBar載せたようなUIですしね。。。
あと、すぐ気づくと思いますが、MicroSDカードが同梱されていないので別途用意する必要があります。HOME+電源ボタン同時押しでキャプチャ撮影可能ですが、外部ストレージが無いと保存してくれません。

感想

たぶんアプリ開発はしないなー。
HTML5のお勉強がてらと思ってポチりはしましたが「じゃあFirefoxOS向けにアプリ開発するか!」とまでモチベーションが高まらず。もし何かをHTML5で作り、そのついでに…ならアリかと思うに留まりました。

余談

最近買ったゲームで、ヒロインの好感度を上げていくと義妹じゃないのに途中から「お兄ちゃん」て呼んでくれ始める神ゲーでした。

最近のWindowsストアアプリ開発するとナニガシ貰えるよー的なまとめ

なんか色々貰えてお得だよ。的な胡散臭さとか怪しさとか必死さとか色々思うトコロはありますが、ど、どうなってるの...?という事で整理してみました。

企画名 貰えるもの 貰える条件 開催期間
Windows Phone 開発者のための Windows ストア チャレンジ! Windows Phone開発者登録トークン(1年間延命券) Windows Phone開発者登録していて、期間内にWindowsストアアプリをストアへリリースすること 2013/03/12 〜 2013/05/10
新規Windowsストア アプリ開発者登録補助プログラム Amazonギフト券5000円分(※先着300名) 期間内にWindowsストアアプリの個人開発者登録を行い、アプリを1本応募すること 2013/04/01 〜 2013/05/0712
しょう子からのWindowsストア登録お祝い 1アプリ毎にAmazonギフト券1000円分(※1開発者につき100アプリが上限) 期間内にWindowsストアへアプリを公開すること 2013/03/08 〜 2013/05/0812

いやー、いろいろありますね!

UIPageViewControllerの使い方 -Tips-

以前にブログで書いた「UIPageViewControllerの使い方 -基礎-」の続きとなります。前は基礎的な使い方を説明しましたが、今回はXcodeテンプレートに無い機能や豆知識について触れていくTips編となります。以前のプロジェクトを度々参照するので、まずは前回のブログ内容からお読みください。

ページ移動を横スクロールのアニメーションにする

デフォルトは紙をめくるようなアニメーションですが、横へスクロールしてページ単位で切り替えるアニメーションにする事がiOS6.0以上なら可能です。
なお、UIScrollViewで内部Viewを横に連ねて横移動させた時とアニメーションが同じです。あえてUIPageViewControllerで行う理由はページ移動をコントロールしやすい点、設定切り替え等で紙めくりとの共存可能な点です。

// インスタンス生成
self.pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];

お分かり頂けたでしょうか?initWithTransitionStyleに指定する引数が以下の2種類存在し、それを返ることでアニメーション方法が切り替わるのです。

定数名 アニメーション方法
UIPageViewControllerTransitionStylePageCurl 紙めくる
UIPageViewControllerTransitionStyleScroll 横スクロール

ページめくり方法を右開きにする

デフォルトは左開き(右から左に向かってページを移動)ですが、右開き(左から右に向かってページを移動)するよう変更する事が可能です。国内向けの電子書籍ならば右開きにする必要があり、必須対応となるでしょう。
対応箇所は2箇所あり、以前のブログ内容を参照しながら内容を記載します。

RootViewController.m
// ページめくりに関するオプション設定
NSMutableDictionary *opt = [NSMutableDictionary dictionary];
[opt setObject:[NSString stringWithFormat:@"%d", UIPageViewControllerSpineLocationMax] forKey:UIPageViewControllerOptionSpineLocationKey]; // デフォルト値を設定したい場合は UIPageViewControllerSpineLocationMin になります

// インスタンス生成。optionsに先ほど準備したoptを設定
self.pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:opt];
ModelController.m
// UIPageViewController上で右スワイプした時に呼ばれるメソッド
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
    // ページの整合性をチェック
    // 左開きか右開きかでここの処理は変わり、以下のコードは左開きの場合の処理です
    NSInteger page = [self indexOfViewController:(MangaDataViewController *)viewController];
    if (page == NSNotFound) {
        return nil;
    }
    if (page == [self.pageData count] - 1) {
        return nil;
    }
    page++;
    
    // 前述したようにページのインスタンス生成を行う
    return [self viewControllerAtIndex:page storyboard:viewController.storyboard];
}

// UIPageViewController上で左スワイプした時に呼ばれるメソッド
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
    NSInteger page = [self indexOfViewController:(MangaDataViewController *)viewController];
    if (page == NSNotFound) {
        return nil;
    }
    if (page == 0) {
        return nil;
    }
    page--;
    
    return [self viewControllerAtIndex:page storyboard:viewController.storyboard];
}

**ページ周囲に余白を入れる
UIPageViewController内部で縁に余白を持たせることが可能です。<br>インスタンス生成時に、先ほどのページめくりでも設定したオプションを使って余白を指定します。
>|objc|
// ページめくりに関するオプション設定
NSMutableDictionary *opt = [NSMutableDictionary dictionary];
[opt setObject:[NSNumber numberWithInt:5] forKey:UIPageViewControllerOptionInterPageSpacingKey]; // 幅5ほど余白をいれる

// インスタンス生成。optionsに先ほど準備したoptを設定
self.pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:opt];

ページをジャンプ移動

ページをまたいで移動可能です(例:1Pから20Pへジャンプ移動)。コード中のmodelController変数はUIPageViewControllerの初期化を行った時のModelControllerインスタンス変数です。

// 20Pへジャンプ移動
RootViewController *viewController = [modelController viewControllerAtIndex:20 storyboard:self.storyboard];
NSArray *viewControllers = @[viewController];
[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];

現在表示中のページ番号を取得

modelController変数は先ほどと同様。なお、ModelController.mコード上のindexOfViewControllerメソッドを前回ブログ内容と同様にしている必要があります。

int pageNo = [modelController indexOfViewController:[self.pageViewController.viewControllers objectAtIndex:0]];

まとめ

だいたいこんなところだと思います。
あとは画像の拡縮が必要だったり、タップ/ダブルタップでイベント発生させたい場合は自力で頑張ってみてください。UIPageViewController関係なく他で行なっている事と同様に行えば実装可能な内容です。

Android4.2以上でJavascriptからJavaメソッドをコールするには

セキュリティ絡みの問題でJavascriptからアプリ上のJavaメソッドをコールする対象へ@JavascriptInterfaceアノテーションを付ける必要があります*1。このアノテーションを付けないとAndroid4.2以上の端末ではJavaメソッドがコールされません。以下、付けてみたサンプルコードです。

// getHogeメソッドはJavascriptからコールされる対象
public class LinkInterface {
    @JavascriptInterface
    public String getHoge() {
        return "hoge";
    }
}

この時にProguard設定に注意する事があります。何の設定もしていないと暗号化され、アノテーションが無効になってしまいます。そこで以下の1文をproguard-project.txtもしくはproguard.cfgに追記すればOKです。

-keepattributes *Annotation*

*1:詳しくは[http://developer.android.com/about/versions/android-4.2.html:title=Android4.2のリリースノート]を参照

UIPageViewControllerの使い方 -基礎-


iOS電子書籍のようなUIを作りたい場合、UIPageViewControllerという素晴らしいUIが存在します。これを使えば、ページ移動時に「ページをペラッとめくってる」や「横へスクロール」するようなアニメーションが簡単に実装できちゃいます。とは言うものの、UIPageViewControllerは構造が少し複雑で、それを把握せずに実装しちゃうと意味不明な詰まり方をします。
そこで基礎編/Tips編と分けて自分なりに整理して書き残します。今回は基礎編なので、ざっくりとした考え方と使い方の説明となります。

UIPageViewControllerで何が出来るか

何が出来るのかについて列挙します。

  • 複数の画面を1画面上に配置し、スワイプする事でページを閲覧する
  • ページをめくるようなアニメーション
  • ページを左右へスクロールするアニメーション(iOS6以降((iOS5でも行いたい場合はUIScrollViewで別途実装する必要があります)))
  • ページ移動方法は、右開き(左へスワイプしてページ移動)と左開き(右へスワイプしてページ移動)
  • 任意ページへの移動
  • 見開きページ(2ページ合わせて表示)

テンプレートを眺めてみる

UIPageViewControllerを知るにはAppleが用意しているテンプレートのコードを追う方法が一番手っ取り早いです。
Xcodeの新規プロジェクト作成から「Page-Baseed Application」を選択して、まずはテンプレートを元にしたプロジェクトを作成してください。

テンプレートのソースファイル構成

作成されたソースファイルを確認してみてください。Xcode ver 4.6.1では以下のように作成されます。

  • RootViewController
    UIPageViewControllerを扱うViewControllerです。ここでインスタンス生成しているので、設定等はここに書かれています。
  • ModelController
    ページを管理するクラスです。各ページを表示する時にページ毎にDataViewControllerがインスタンス生成され、ページに対してデータを送りたい場合はここに定義する事になります。他にはページの「進む/戻る」を制御してたりもします。
  • DataViewController
    表示するページです。表示させたいページ内容に応じ、ここに定義します。

図にすると以下のような関係になってます。


UIPageViewControllerのインスタンス生成と初期化

MainStoryboard_iPhone.storyboard と RootViewController.m を参照ください。storyboard上に設置されたUIPageViewControllerをRootViewController.mのviewDidLoadメソッド内で以下のような初期設定が行われています。インラインで説明すると書きにくかったのでコード上にコメントを書いてます。

// インスタンス生成
self.pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil]; // 指定内容に関しては次回説明します
// UIPageViewControllerDelegateプロトコル
self.pageViewController.delegate = self; // ページめくり開始/終了などのdelegate methodがあるのでコントロールしたい場合は設定しとく
// 表示するページを設定
DataViewController *startingViewController = [self.modelController viewControllerAtIndex:0 storyboard:self.storyboard]; // storyboard上で定義しているページ画面
NSArray *viewControllers = @[startingViewController];
[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:NULL];
// 管理クラスを設定
 self.pageViewController.dataSource = self.modelController;
// addChildViewControllerした後にする必要があるオマジナイ的なヤツ
[self.pageViewController didMoveToParentViewController:self];
// ジェスチャー設定
self.view.gestureRecognizers = self.pageViewController.gestureRecognizers; // UIPageViewControllerが行うジェスチャーをInterceptする事もできる
ページをめくり

ModelController.m を参照ください。先ほどと同じようにコードにコメント書いて説明します。

- (id)init
{
    self = [super init];
    if (self) {
        // ページに表示するデータの初期化。サンプルアプリが月名をページに表示してるので、ここは月データを扱ってるというだけ
        // RootViewController.mでModelControllerのインスタンス生成時にページデータを渡し、ここでページデータを保持する方法を取る
        NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
        _pageData = [[dateFormatter monthSymbols] copy];
    }
    return self;
}
// ページのインスタンスを生成するメソッド。ページ移動した時などに使われる
- (DataViewController *)viewControllerAtIndex:(NSUInteger)index storyboard:(UIStoryboard *)storyboard
{
    // indexの値がページ情報の件数と整合性が取れてるか確認してるだけ
    if (([self.pageData count] == 0) || (index >= [self.pageData count])) {
        return nil;
    }
    
    // ページのインスタンスを生成。ここでinitで保持しておいたページ情報を受け渡す
    // これでDataViewControllerの内容がUIPageViewController上に表示される
    DataViewController *dataViewController = [storyboard instantiateViewControllerWithIdentifier:@"DataViewController"];
    dataViewController.dataObject = self.pageData[index];
    return dataViewController;
}
// privateなメソッドで、引数のvewControllerがどのページかを返してるだけ
- (NSUInteger)indexOfViewController:(DataViewController *)viewController
{
    return [self.pageData indexOfObject:viewController.dataObject];
}
// UIPageViewController上で右スワイプした時に呼ばれるメソッド
// 勘違いしやすいのは名前にbeforeと付いているからといって必ずしも前ページへの移動の時だけに呼ばれるという訳ではないです
// 左開きの時は戻る場合、右開きの時は進む場合に呼ばれます
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
    // ページの整合性をチェック
    // 左開きか右開きかでここの処理は変わり、以下のコードは左開きの場合の処理です
    NSInteger page = [self indexOfViewController:(MangaDataViewController *)viewController];
    if (page == NSNotFound) {
        return nil;
    }
    if (page == 0) {
        return nil;
    }
    page--;
    
    // 前述したようにページのインスタンス生成を行う
    return [self viewControllerAtIndex:page storyboard:viewController.storyboard];
}
// UIPageViewController上で左スワイプした時に呼ばれるメソッド
// 全てにおいて先ほどのメソッドと逆になります
// 左開きの時は進む場合、右開きの時は戻る場合に呼ばれます。
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
    NSInteger page = [self indexOfViewController:(MangaDataViewController *)viewController];
    if (page == NSNotFound) {
        return nil;
    }
    if (page == [self.pageData count] + 1) {
        return nil;
    }
    page++;
    
    return [self viewControllerAtIndex:page storyboard:viewController.storyboard];
}
ページ表示

ページ(DataViewControllerクラス)はModelControllerにより、ページの数分だけインスタンス生成される事はここまでの説明で分かったかと思います。あとは受け渡されたデータを元に好きにページを表示するだけで良いです。
インスタンスは2ページ移動すると破棄されます。

まとめ

サンプルを眺めたお陰でUIPageViewControllerの構成が理解出来た気になれたかと思います。
次のTips編ではサンプルプロジェクトをベースに電子書籍アプリでやりたいであろう機能をTips形式で列挙していく予定です。

追記(2013/04/22)

Tips編を書きました。

iOSアプリの申請時や申請後のアレコレ

割と知られてないだろうなーって事をザクっとまとめて書き残しておきます。

特急審査

iOSアプリの審査ってだいたい5営業日くらいですよね。「ふええ…この日までに公開しないといけないのに><」という全世界の幼女や、「スクレイピングしてたサイトの仕様変更でアプリが正常動作しなくなっちまったぜー」という野郎のために通常より早く申請して貰えるようAppleに懇願する事が出来ます。
やり方は以下サイトへアクセスして、アプリ情報と特急審査してほしい理由を英語で書いてあげればOKです。ただし、必ず特急審査してくれる訳ではなく、理由が弱かったり審査に忙しかったりすると拒否られちゃいます。でも、受理されれば1・2営業日くらいでチャチャッと審査してくれます。いちかばちかのファイナルウエポンです。
Request an Expedited App Review

申請後に5営業日以内にレビューされる割合

App Store Review Status」欄に5営業日以内にレビューされるかどうかの割合が表記されています。これで今混んでるかどうかが分かります。
New and Announcements for Apple Developers

そのreject異議あり!(2013/04/02追記)

審査結果に納得がいかない場合、通常はreject内容が記載されている「Resolution Center」ページに記載していくのですが、ここに記載すると審査した人との一方通行なやり取りで終わってしまう事があります。そんな時はアプリ委員会に不服を申し立ててみましょう。おそらく他の人がレビューし直してくれるので、結果が覆る可能性があります。ただし、結果が返ってくるまで一週間くらい待つ必要があり。
Submit an Appeal to the App Review Board

審査してる人にテレホン(2013/04/02追記)

reject内容が意味不明で「Resolution Center」や「Submit an Appeal to the App Review Board」でやり取りしても意味分からない!って時は「Resolution Center」ページに「ちょっと何言ってるか分からないんで日本語で電話しませんか?」というメッセージを書き込めば電話してくれるかもしれません。その際はアプリ詳細に電話番号を書く必要があります。国際電話なので090から始まる場合「+81-90-0000-0000」、080から始まる場合「+81-80-(ry」といった感じで。
テキストの場合はやり取りに1ターン1週間、しかも答えてくれない場合あり。なのに対し、電話だとすぐ答えてくれるし、答えられない質問に対しても割りと明確な理由を教えてくれます。。。でも、reject結果が覆ることは稀なのでしょうね。

公開アプリのバナー表示

公式サイトのLink Makerからリンク付きバナーを取得できます。シンプルな公式バナーを利用したいならこれで!
LinkMaker
他には非公式でAppHtmlメーカーとかありますね。アプリのレビューサイトとかがよく使っているのはコレや、この類似サービスや改造したもののようです。
AppHtmlメーカー

AppStoreのアプリURLの短縮化

公式サービスの「AppStore.com」を使えばのような「appstore.com/iTunes Connect上のアプリ名」という表記でアプリ詳細へアクセス可能です。
例えば、「ぼっちりとり」アプリだと以下のようになります。
https://itunes.apple.com/us/app/bocchiritori/id625153931?l=ja&ls=1&mt=8
http://appstore.com/bocchiritori
先頭の「http://」はあってもなくても構わないようですが、こういったページに貼る際はhttp://を付けないと自動リンクされないので付けてます。
まぁ他の短縮URLサービス使えばいいかもしれませんが、公式を使ったほうが貼られてる表記名を見た時にユーザが安心するでしょうね。たぶん。

初のiOSアプリ「ぼっちりとり」をリリースしたよ

第一作品目の「MyReview」が去年末に敗れ、第二作品目の「nicoマンガ」は先月敗れた…が、第三作品目の「ぼっちりとり」は一回もrejectされずにリリースできました!
DLは以下のバナー先から行えます。

アプリ特徴


「ぼっちりとり」とは、一人ぼっちで「しりとり」を行うゲームの事を指し、孤独と闘いながら単語の末尾に「ん」が付かないよう細心の注意を払う必要があるハードクールなゲームです。
しりとりで入力する単語は、形態素解析向けの「NAIST Japanese Dictionary」辞書の名詞データ(人名は除く)と、はてなキーワードにある単語を有効とし、それ以外は存在しない単語扱いとなります。

あとランキングシステムを組み込んでおり、どれだけしりとりが続いたかをスコアボードに刻めます。このスコアが高ければ高いほど一人ぼっちで続けた回数となるため、孤独値とも言えるでしょう。

今回気をつけた点

Game CenterのLeaderboardを使う時に気をつけた事は以前書きました
他に気をつけた事は、時間掛けてアプリや画面設計や特殊機能を実装するとrejectされた時の落胆が激しく、今回は三度目と言うことで、全てにおいて力の限り力を抜きました。

ナゾ現象

開発時にサンドボックスアカウントを作成したりしてたiPhoneに対し、リリースしたアプリをインスコしてメイン画面のランキングボタンを押下するとフリーズします。。。サンドボックスアカウントでログイン中でも無いのにナゾです。テスト機ではないiPadやシミュレータ上だと問題ないので何かが競合してのエラーだとは思うのですがナゾです。