2011年12月20日火曜日

CoCafeの作り方(Googleプレイス編)

先日のエントリで、マップのUIについて書いた、CoCafeの作り方。
今回はGoogleプレイス編です。

CoCafeでは、位置情報から近くのカフェ情報を取得して表示しています。
カフェの情報は、Google Places APIを使って取得しています。

Google Places API は、1,000 リクエスト/日 の利用制限がありますが、
クレジットカードの登録により個人情報の登録をすれば、 100,000 リクエスト/日 を利用できます。
(クレジットカードの登録はするけど、請求は0円)
あと、行う必要があるのが powered by Google のロゴの表示 。
マップ上に表示するには、Googleマップの上で行わないといけない ようです。

お店の検索条件として以下のものが使える。
location (required):緯度・経度
radius (required):検索の範囲(m)
keyword (optional) :キーワード
name (optional):お店の名前
types (optional) :ここにあるタイプ。 | 区切りで複数指定可能。

なんとなくいけそうだな ってことで使用し始めたのですが......一回浮気しようとしたタイミングがありました。
理由は ... お店の情報が綺麗にカテゴリ分けされていない ため。

Googleプレイスでは、いろんなタイプでタグ付けされているのですが、cafe のタイプで検索しても、近くのスタバとかベローチェのお店が表示されない...
そのお店は登録されているのですが、設定されているタイプをみると establishment のタイプのみ。
いっぱいタイプがありすぎるのもどーかなーとも思うのですが、establishment だけってのも..なぁ...
とはいえ、establishment を入れるとほとんどの建物がひっかかちゃって、実用的にならない。

じゃぁ、カフェ とか スターバックス、ベローチェ、ルノアール... って有名どころの名前を一気にnameで条件指定して検索かけたらどうだろう?と試したのですが....
最初の方に指定されたキーワードのものが優先されてしまい、後のほうに指定したものは、引っかからなくなる。
Google検索と同じ感じで、はじめに指定されたキーワードの方が重要だ と認識される感じ。

1回の検索で返ってくるお店の数の上限が決まっていて、rate(評価)の高いお店から多分優先的に返すようなんですが、指定している範囲を超えて情報が返ってくるので、より 引っかからない度合いが顕著に...。

これを解決するには、取得したいお店名 ごとに1回のリクエストを投げて情報を取得するしかないのか... と一時期実装していたのですが、10数回一気にリクエストを投げることになり、API制限も気になってしまう結果になり断念。

そこで、食べログAPI はどうだろう? と問い合わせしてみました。
有料アプリになるかどうかはまだ判断しきれていないタイミングだったので、
> 個人で作成しているアプリだけど、200円程度の有料アプリになる可能性もあるがあるが、つかえるか?
という内容の問い合わせをしたところ
> 商用利用はお控え下さい
の返答が。
サイトをみると "法人の方は別途お手続きが必要" とあるのですが...返答メールの内容には、別途お手続きをしてください... 云々的な文言は一切なかったので個人だと対応もできないのかな。残念。

ぐるなびAPIはどうだろう? と見てみたのですが、
XMLのレスポンス...か...
でも!いい情報がとれるなら...と見てみたのですが、カフェ情報となるとやっぱり少なかった。
居酒屋とか食事メインの情報サイトですものねー。そうだよなー。 と断念。

# 結局、お店の登録内容や数が一番いいのは foursquare なんじゃないかとも思ったりもしますね...
# 試してないのですが、cafe ってカテゴリだけで近くを検索とかできるのかしら...
# OAuthで認証していないアカウントの場合の制限はどれくらいになるのかしら...

...で、Googleプレイス に戻って来ましたw

CoCafeでは、name(お店の名前)での検索をできるようにしているのですが、
お店の一部分を指定しても引っかからなかったり、英語表記のお店が引っかからない...と思ったら、カタカナにしたら引っかかったり...とちょっとイマイチ感。
Googleプレイスにお店の登録はあるのに、探しきれないという場面が多々あります。

Google Places API を改めてみると、Place Reportというのがある。
Places that have been added by your application can also be deleted, until they have been moderated. Once moderated and added into the full Place Search results, a Place can no longer be deleted. Places that are not accepted by the moderation process will continue to be visible to the application that submitted them.
と。
ちょうど近くにオープンした サンマルクカフェ が登録されていなかったようなので、登録してみた。
...が、自分だけが見れる登録情報の状態?で GoogleプレイスのURLも発行されない。
暫く日をマタイだ後.... Googleプレイスにサンマルクカフェが正式に入ってきた! ....が、自分が登録したサンマルクカフェも表示されて2重表示状態に!!! むぎゅー。自分が登録した方は自分で削除しましたよ otz

CoCafeとしては、Googleプレイスのデータベースをよりよいものにする情報を提供したい所ではありますが、今の状態だと、為す術がないですね...無念。

Googleプレイスの愚痴が多くなってしまっていますが...
お店がオープンした/閉店した というのも (タイムラグはありますが)反映されるし、
GoogleプレイスのWEBURLをみると、写真情報や評価/口コミも見れるし。
Googleマップで検索したお店の情報はGoogleプレイスの情報だと思うので、お店側も最新情報を載せよう!と思ってくれるだろうし。インフラとしてはとても最高。

正確な情報を、早いタイミングで更新するのはとっても無理だとは思うので、使用ユーザの手をうまーーく借りることができると良いんですけどもねー。
foursquareのお店情報って充実してるなー って思うのは、使用ユーザの手を借りているからなんだろうと思うので。

そうすると、2重登録は発生するし、
ユーザがお店を見つけられずに "閉店" としてしまって、訴えられることもあるでしょうが...
Googleさんなら出来るよ。がんばって!
• • •

2011年12月17日土曜日

CoCafeの作り方

先日リリースしたCoCafeですが、苦労したこと? を参考までにまとめてみようかと思います。

マップでのピン表示

CoCafeではMapを MKMapView を使って表示しています。
特徴的な所は、Mapに表示するピン。
ピンはデフォルトで3種類ぐらいの色を指定出来るピンが用意されているのですが、CoCafeでは有名所のカフェは色がそれなりに合うアイコンを用意してピンとして使用しています。

ピンは MKAnnotationView を拡張して自作しています。
MKAnnotationViewのimageプロパテイで、UIImageを設定するとピンの画像を変えることができます。

マップ上のピンは、端末の向きが変わると、画像の中心を回転の中心として画像の向きが変わります。
そのため、画像は、ピンの先が中心になるように画像を用意します。

アイコンは画像で用意します。画像はRitenaディスプレイ用に2倍の大きさで用意。

ピンをクリックした時に表示する 吹き出し部分 も自作したかったのですが、
簡単に変える方法は見つからず、ゴリゴリ書かないといけないっぽい感じ。
途中まで書いていたのですが、そこまで手間をかけることでもないよなーーー..ということで、CoCafeでは左側にアイコン載せるだけのカスタマイズをしました。


左側のアイコンは、MKMapView の leftCalloutAccessoryView プロパティで 自作のUIViewを設定
右側のボタンは MKMapview の rightCalloutAccessoryView プロパテイで UIButtonTypeDetailDisclosure のボタンを設定
しています。

UIViewで指定位置に配置できるので、全然無理がないカスタマイズw
ゴリゴリ系を諦めた理由の1つにMapViewがバージョンアップとともにインターフェースが変わる予感がした.. というのもあります。(勝手な想像です)

現在位置に移動

iPhone標準アプリのマップにある、現在位置を表示←→コンパス機能っていいですよね。
方向音痴の私としてはとても大切な機能。これがiOS5で使えるようになっていたので早速つかってみました。


作成方法は
MKMapView *mapView;
MKUserTrackingBarButtonItem *trakingItem = [[MKUserTrackingBarButtonItem alloc] initWithMapView:mapView] ;
とボタン作成時にMapViewを紐付けるだけ。
とっても簡単。

...だけど、画面を切り替えた時に、コンパスモード→現在位置を表示 に戻したいのに戻す方法が分からない。
これ、結局方法分からず。ご存知の方いらっしゃったら教えて下さい...

ペロっと途中までめくれるページ

iPhone標準アプリのマップにある、マップの種類(航空写真とか)を切り替えるページ。
ぺろっとちょっとめくれて良い感じですよね。
これをCoCafeにも!と思って入れています。CoCafeでは表示するカテゴリの切り替えができるようになっています。

このエフェクトは pageCurl というものになるのですが、途中で止める方法がはじめ分からず。
途中で止めないならば、
[UIView setAnimationTransition: UIViewAnimationTransitionCurlDown forView:self cache:YES];
とかが使えるっぽいけど...

CATransition *transition = [CATransition animation];
    [transition setType:@"pageCurl"];
    transition.duration = 0.35;
    transition.endProgress = 0.8;
    transition.removedOnCompletion = NO;
    transition.fillMode = kCAFillModeForwards;
    [self.view.layer addAnimation:transition forKey:@"curlup"];
でできるのは分かったのですが、リジェクトされるかもよ? という記事もあり。
たしかに、typeで指定できる定数として
kCATransitionFade
kCATransitionMoveIn
kCATransitionPush
kCATransitionReveal
があるっぽいけど、pageCurl がない。たしかに使っちゃダメそうな...
解決できず。このままリリースしちゃった。とおちゃった。ごめんなさい...ごめんなさい...><

なんか、UIばっかりの話になっちゃった。
次回はGoogleプレイスAPIについてちょっと書こうかな。
でわでわ。
• • •

2011年12月10日土曜日

CoCafe リリースしました!



CoCafeが正式リリースしました!
サイト http://cocafe.sorausagi.org/
AppStore http://itunes.apple.com/jp/app/cocafe/id483713206

CoCafeは、カフェの電源/WiFi/分煙などの情報をみんなで共有するiPhoneアプリです。
地図上にはGoogleプレイスのお店情報が表示されるので、大手チェーン以外のCafeでも表示されます。
お気に入りのカフェをぜひ見つけてくださいー。

ひとつの特徴としては、WiFiの設定がとても細かい事ですw
モバイルWiFi (WiMax, EMOBILE, Xi...) WiFi(mobilepoint, FON, Wi2...)など個別に電波強度を設定できます。
すっごく日本限定ですけども、モバイルWiFiを持ち歩く人には嬉しいはずっ

iPhoneアプリはCrossBacklogに続く第2弾目。
今年の目標の1つだったので、ギリクリア!

AppEngineのサーバー側を作ってくれた @shin1ogawa
アイコンを作ってくれた @horiuni
にも感謝!

現時点ではまだAppStoreの検索には引っかかってくれなくて残念ですが...
あと、アクセス数が伸びたらどんだけGoogleから請求がくるのかもとても心配だったりも...ww
• • •

2011年11月12日土曜日

WordPressのカスタム投稿タイプとパーマリンク

WordPressはブログシステムですが、いろんな拡張ができる。
拡張の1つが、カスタムフィールド と呼ばれるもので、投稿記事に独自の属性を追加できるもの。
さらに、WordPress3.0からは カスタム投稿タイプ/カスタムタグソノミー というものが独自に追加できるようになっています。
WordPressは、[投稿]と[固定ページ]の2つのページの種類がありますが、それを増やすことが出来るのが カスタム投稿タイプ。
それらのページに [Category]という分類を設定できるのですが、[Category]以外の分類を増やす事が出来るのがカスタムタグソノミー。
カスタム投稿タイプ/カスタムタグソノミー を使うと、ブログシステムの枠を超えて、簡単なシステムを作る事ができるようになります。
記事を投稿する画面や、分類毎にページを作成するphpの振り分けはwordpressに任せてあとは自作という感じに。

ここ、3ヶ月ほどWordPressを使ってみて、カスタム投稿タイプ/カスタムタグソノミー も使ってみたりしていたのですが、よくハマったのがパーマリンク。
WEBページでのパーマリンクはREST風にちゃんと設定したくなるので...
WordPressでは、デフォルトの記事のリンクは

http://localhost?post_type={投稿タイプ}&p={投稿のID}

のような感じになっています。これを

http://localhost/sample/test

のようなリンクにするために、URLのRewrite をガシガシ行なって表示します。
WordPressではこのRewriteルールを自動的に設定or管理画面より設定できるようになっています。

たとえば、以下のような設定がWordpressから設定されます。
category/(.+?)/page/?([0-9]{1,})/?$' => string 'index.php?category_name=$matches[1]&paged=$matches[2]

WordPressのデフォルトのRewriteルールの設定は、管理画面のパーマリンクの設定を更新した時に行われると思うので、なんかリンク系を変更した時 とか データを他のサーバーに移行した時 などでは とりあえず更新しておくのが無難。

まぁ、それでも、うまく動かなくなったり、独自のルールにしたくなったり するのでお相手する必要があるのですけども。

ハマったことを中心に。

カスタムタグソノミーもパーマリンクの一部にしたい

デフォルトの投稿タイプ(post)では、カテゴリをパーマリンクの一部にできます。
だが、カスタム投稿タイプのときは...一番いい方法かどうかは不明ですが...以下でできます。

register_post_type で、
'rewrite' => array( 'slug' => CUSTOM_POST_TYPE.'/%'.CUSTOM_TAXONOMY.'%', 'hierarchical' => false ),
と指定しておいて、
add_filter('post_link', 'rating_permalink', 10, 3);
add_filter('post_type_link', 'rating_permalink', 10, 3);

function rating_permalink($permalink, $post_id, $leavename) {
    if (strpos($permalink, '%'.CUSTOM_TAXONOMY.'%') === FALSE) return $permalink;

    $post = get_post($post_id);
    if (!$post) return $permalink;
    if ($post->post_type != CUSTOM_POST_TYPE) return $permalink;

    $terms = wp_get_object_terms($post->ID, CUSTOM_TAXONOMY);
    if (!is_wp_error($terms) && !empty($terms) && is_object($terms[0])) $taxonomy_slug = $terms[0]->slug;
    else $taxonomy_slug = 'not-rated';

    return str_replace('%'.CUSTOM_TAXONOMY.'%', $taxonomy_slug, $permalink);
}
無理やり埋め込んであげている感じですねw
ただし、上記の方法だと、投稿IDをキーにパーマリンクを取得(get_permalink)する時にうまく反応してくれないことがあるので注意。

カスタム投稿タイプのパーマリンクの設定


カスタム投稿タイプの場合、register_post_type という関数を使って登録した後、
flush_rewrite_rules();
を行うと、Rewriteルールを追加してくれます。逆にこれを実行しないと追加されない。

まぁココまではいいのですが、複数のカスタム投稿タイプをつくった場合、登録した後にすぐに上記を複数回実行すると、環境によっては反映が不十分で、パーマリンク設定が未設定のものができてしまう現象に遭遇。
カスタム投稿タイプの登録(register_post_type)は、add_action('init', ***)のアクションフックで処理し、
flush_rewrite_rules は initの後に実行される add_action('wp_loaded', ***)で1回実行することにより解決しました。

あるはずのリンクが404になる

http://localhost?post_type={投稿タイプ}&p={投稿のID}
のような形式ならみれるけど、
http://localhost/sample/test
のような形式だと404になるのであれば、RewriteRuleがうまく設定されていない問題。

あるある。めっちゃよくある。

こういう時は繰り返しになるけど、

- 管理画面のパーマリンクを更新してみる
- カスタム投稿タイプの時には flush_rewrite_rules が実行されているかみる
- global $wp_rewrite を var_dump してルールの中身を確認してみる

な感じでさぐれば解決できるはず。たぶん。

http://localhost/****/****/page/2 のようなURLで2ページ目が表示されないという場合は
他のマッチパターンに引っかかってしまってダメな時もあります。
**** の部分が自由に設定できるような時は、page という名前の投稿名なのか pageの設定なのかが判断つかないよーな感じ。
WordPress.org Using Permalinksを見ると、
http://www.example.com/page/2/
http://www.example.name/category/categoryname/page/2/
http://www.example/year/month/day/page/2/
http://www.example/year/month/page/2/
の例が書かれてますね。

パーマリンクを変えたい

よくあるのが、カテゴリページのURLの category というのを削除したいというパターン
http://localhost/cateogry/fruit

http://localhost/fruit
Top Level Categories ってプラグインを使うと消すことができます。

もっと自由に設定したいんだ! というときには Rewriteルールを書いちゃう手も。
add_filter( 'rewrite_rules_array','my_insert_rewrite_rules' );
function my_insert_rewrite_rules($rules) {
$newrules = array();
$newrules['fruit/?$'] = 'index.php?category_name=fruit';
return $newrules + $rules;
}
とか。



パーマリンクの形式は事前に検討して、
全体的に整合性がとれるか? (=自分で正規表現でマッチパターンがかけるか?)
をしておくのは大事。
これができていれば、最悪なんとか頑張ればできるはず...と思う。
• • •

2011年11月10日木曜日

仕事環境を変えてみた

iPad , Apple Cinema Displayが導入され、
以前の環境

が手狭になり、拡張いたしました。


なかなか快適な感じに。

MacBookAir 11chのThunderboltから、PLANEX Mini Displayport ->DVI端子 をつかってCinemaDisplayに無事に接続できました。
iPadには、degital AV Adapter を購入して、HDMI経由でTVに出力もしてみた。

ホワイトボードのアプリを表示してみた図
縦横比が違うので、左右が黒くなっております。
横で見たいときにはiPadを横にしないといけない。

YouTubeの動画をテレビに出力してみた図

画面全体にわたって表示された。なかなか綺麗。
Potcast(ビデオ)を再生しても画面全体を使ってくれる。
iPadは縦にしていても大丈夫。
TVに繋いでいるスピーカー経由で音を聞くので、音も良し。

Huluをアプリから見てみると... 通常のアプリと同じ感じで、単純なミラーリングっぽい。
HuluはPS3から見たほうがよさそう。

iPadスタンド用にApple純正のドックを買ってみたのですが、これが失敗。
縦置き専用なので、横置きができぬ。まぁいいけど。
• • •

2011年10月13日木曜日

自分のことは一番分かりません

転職してから、自分のことを考えたり、聞かれる事が増えた。

それまでは、与えられた仕事について 楽しい とか 楽しくない とか感じるくらいしか考えてなかったなぁと思う。
新規の案件であれば、提案とかもできて楽しいな とか、保守/メンテ/バグとり は楽しくないな とか。
○○言語をやりたい とか系はあったけどww
自分を説明するのも、「javaを長年やってましたー」みたいなのが自分の説明だった。

どんなタイプ?
何がしたい?
何をしていると楽しい?
と改めて聞かれると、言葉に詰まる。

RPGで考えると、主役系じゃない....勇者でも賢者でもない。
錬金術師みたいな感じかしら。
これが欲しいって言われて作って、喜ばれると幸せ。

属性は ミーハー。飽き性。おっちょこちょい。ビビり。
たまにビビリを脱却しようとするが、度合いが分からず、
"ええ! そんな事したの?" と言われるような事をしでかす事がある。

HPは多くないけど、瀕死になるとアドレナリン上がります。
暇だと即死しますw
• • •

2011年9月20日火曜日

CoCafe(仮) アプリ作成中


CoCafe(仮) iPhoneアプリを作成中です。(サーバ側は @shin1ogawa に協力してもらっています)
ゆっくりCafeでコーヒーを飲んで、パソコンを触りたい! ときに、Wifiが接続できるかどうか... というのは大問題。
ここではWiMax大丈夫! というのを保存したい... というのを目的に作成中のアプリです。

Map上でお店情報を表示。

そのお店の情報を表示

お店情報を登録

自分が登録したお店情報を一覧表示

お店のロゴを使いたかったけど、試しにルノアールに問い合わせしてみたら返信が返ってこないので... お店ごとに許可をとるのは断念。結局、自分で作成w

食べログAPIも使おうかと思ったんだけど、問い合わせをしてみたところ、個人利用では、
* API制限(200/日)というのを大きくしてもらう検討とかはダメ (不正利用が大きくて問題になっているらしい)
* 有料アプリでの利用は無料のAPIでは使用できない (企業としての登録が多分必要な感じ)
* APIが廃止になるかもしれないのも考慮してほしい
....とのことで、使用は断念。
で、お店情報はGooglePlace APIを使って取得しています。
GooglePlace APIも、アクセス制限があってアクセスが増えると課金される&自分の首を締めるので、有料アプリにするか、広告にするか... なんか検討しないと行けないですがー。

mapでcafeの表示をみていると、
こんなところにcafeが! という発見があったり、
Map上に店が多いと思ったら、こんなところに商店街が! という発見があったりしてなかなか面白いです。

リリースはまだ出来ていませんが、そんなアプリいいな! と思った方は応援いただけると嬉しいです♪
• • •

2011年8月11日木曜日

プログラマからみたWordPress(データ構造)

言葉としてはよく聞いていた、WordPress。
最近少し触ってみたので、プログラマからみた感想を書いてみようエントリです。
しかも、データ構造をみれば何となく中が想像できるだろう...という勝手な視点からw
(まだ1週間しか触っていないので違うところがあったらゴメンナサイ)

まず....WordPress とは
Movable Typeと同じようにブログを書くためのソフトウェア。
MySQLとPHPの組み合わせで動きます。
いろいろカスタマイズできるのですが、「元々はブログを書くために生まれ、拡張されていったのね 」という認識を捨てないのが大事です(笑

登録画面からみたデータ構造


ページとして投稿できるものとしては2種類。
- 投稿 : ブログの投稿用のもの。カテゴリ、タグの設定が可能。親ページの指定はできない。
- 固定ページ : カテゴリ、タグの設定はできない。親ページの指定はできる。

メタ情報として2種類
- カテゴリ : 親子関係がある。
- タグ : 親子関係なし。

投稿、固定ページともに「カスタムフィールド」があり、メタ情報をお好みで追加できる。

DBからみた構造


テーブルの数は11個。思ったより少ない。中身をみてみると... 注目すべきテーブルは5つ。

wp_***_posts

投稿、固定ページ情報が入っているテーブル。
登録内容や動きは違うけど、テーブルは一緒で、"post_type" という属性により切り替わっている。

"post_type" の値は主に以下が入っているっぽい。
- post : 投稿の情報
- page : 固定ページの情報
- nav_menu_item : 設定画面でメニューを設定したときの情報
- attachment : 投稿/固定ページで設定した画像などの情報
- revision : 投稿/固定ページのリビジョン情報

テーブルのPrimaryKeyは「ID」
つまり、上記の情報は種類は違うけどIDはかぶらないはずということ。

また、「カスタム投稿タイプ」によって上記以外の "post_type" のものを作成する事ができたりもします。

PHP内では、get_post というメソッドでID指定すると、このテーブルの情報がとれる。
(論理的にはそうなんだけど、固定ページ用に get_page というメソッドも用意されているので、該当メソッドを使った方がコードの可読性はよい)

もうひとつ、get_posts というメソッドで条件指定で複数のPOSTを取得できるメソッドがあるけど、引数で設定するのはテーブルの項目とは一致しない。
また、post_type のデフォルトは "post" なので、違うタイプは明示的に指定が必要。
(こちらも固定ページ用に get_pages というメソッドが用意されている)

wp_***_terms

カテゴリ、タグ が入っているテーブル。
テーブル項目は
- term_id (Primary Key)
- name (表示名)
- slug (URLの一部として使われる文字)
- term_group (グループ化できそーだけどどこから設定するのか不明)

と4つだけ。term_idがPrimaryKeyだけど、使用者としてはslugがkey項目として使う感じ。
なのでこのテーブルに入っているものでslugは重複したらNG。
(WPの入力画面上で、重複したslugをいれられると、勝手に後ろに -2とかがつく)

PHP内で情報を抜き出すには、
カテゴリの場合は get_category, タグの場合は get_tagというメソッドで、term_idを渡すと取得できるのだけど、その中で呼ばれているのは結局、
get_terms というメソッド。 (想像どおりw)

wp_***_term_taxonomy

wp_***_terms に、カテゴリ、タグの区別情報は入っていません。
区別情報が入っているのがこのテーブル。
テーブル項目は、以下。
- term_taxonomy_id (PrimaryKey)
- term_id
- taxonomy
- parent (親taxonomyのid)
- count

"taxonomy"項目には以下の情報がはいっているっぽい。
- category : カテゴリ
- post_tag : タグ
- nav_menu : 設定画面でメニューを設定したときの情報

つまり、カテゴリとタグは 親がつけれる/つけれない の差があるけど、変更はこのテーブルの値を変えるだけで切り替えができるということ。

「カスタム投稿タイプ」同様、taxonomyも自作できる機能があるのですが、その情報はここに入ります。

wp_***_term_relationships

taxonomyとpostをひもづけているのが多分このテーブル。
テーブル項目は、以下。
- object_id
- term_taxonomy_id
- term_order

object_id は wp_***_postsのID が入っている。(多分)

wp_***_postmeta

画像などのattachmentのID情報、投稿/固定ページで設定できる「カスタムフィールド」の値はこのテーブルに入ります。
テーブル項目は、以下。
- meta_id (PrimaryKey)
- post_id
- meta_key
- meta_value

meta_keyには
ページで指定しているアイキャッチの画像の場合は "_thumbnail_id"が入っています。
カスタムフィールドの場合は、指定したkeyがそのままmeta_keyにはいります。
他にもいろいろメタ情報の登録があるのですが、ぱっと身、WPが自動登録してくれるものは _ から始まるkeyのようなので、カスタムフィールドのkeyには _ から始まらないもの の方がよさそうです。

ページで指定しているアイキャッチ画像は、the_post_thumbnail とかでズバっと画像出力ができるようになっていますが、設定項目を抜き出したい! という時には
「postmetaからIDを取得して、postsから情報抜き出す」ということで可能です。
$post_thumbnail_id = get_post_thumbnail_id($post->ID);
$attachment = get_post($post_thumbnail_id);

カスタムフィールドの値も同様に get_post_customというメソッドが用意されています。


ながながと書いてしまいましたが、データ構造を俯瞰して分かる事としては...こんなこと。

* カテゴリ/タグ には メタ情報追加は構造的に無理!

* 投稿/ページ には 複数のtaxonomy(カテゴリ/タグ)をつけれるが、taxonomyの親は一つだけしか構造的に無理!

* あるカテゴリ/タグに紐づくpost情報を抜き出すのは結構joinしていてコスト高い!
(そのため、内部ではキャッシュしているっぽいコードがある)

* 今後、WPがバージョンアップして機能追加した時は post_type とか taxonomy の値で新しい種類のものが追加される可能性が大きい。
つまり、カスタム投稿タイプ/taxonomy のkey名は、WPがversionアップ(機能追加)してもつけられないであろう名前にしておく方が無難。

* この記事が何回表示されてるとか 値を持ちたい時には、プログラムから wp_***_postmetaに バシバシいれたらよいかも。ただしkeyがかぶらなければ。
pluginも使うかもかもしれない = 違うpluginで同じkey値を偶然に使っていた場合はチョー怖いね。

* リビジョンを保持するようにすると、postテーブルに入っちゃうのでレコード数がガンガン増える!
リリース前は少なくともリビジョンは外して登録しておいた方が何となくよいよーな気もする。

かな。
• • •

2011年7月16日土曜日

退職しました

昨日、ヌーラボを退職しました。

ヌーラボに入社したことは、私の転換期だったと思います。

前職は java ..といっても Swing ばかり7,8年触っていたのですが、ヌーラボではSwingは一切触りませんでした。
seasar2 や、cubby, velocity, jsp ... を使った、いわゆる一般的なWEBアプリを作ったり、Eclipse Pluginを作ってみたり、BlackBerryのアプリを作ってみたり、Flash を作ってみたり、携帯のアプリを作ってみたり、iPadのアプリを作ったり、スマートフォン向けのWEBアプリを作ったり...
会社のメンバーもオープンで活動している方が多く、そんな中で "うっはー 私も頑張らねばな" と思う日々を過ごせました。
また、個人活動としても AdobeAIR を使って 空うさぎ を作成したり、iPhone用のBacklogクライアントとして CrossBacklog を作ったりしました。
特に、空うさぎ は個人的に作成したアプリの中では今のところ代表作。
前職の時に比べたら、いろんなことをしたなー。

残念だったのは、
一人での開発が多かった点。
結局、何かお仕事を完了後、振り返りしたり、打ち上げでぱーっと飲みにいく という事はあまり無かった。
自分がやったお仕事の打ち上げというのは結局1回もなかったような...忘れているだけ?

もう一つの残念というか反省点は、コレ という成果を残せなかったこと。
私は若くはないので残せないといけないのですが... スイマセン。

チームでの開発がしたい とか みんなで向上していきたい というのが、ヌーラボに入った時からの一つの目標というか、希望だったので、最初の頃は勉強会を提案してやってみたりしたのですが尻切れトンボになっていました。

モクモクとお仕事をしつつ、思いがだんだん変わってきた..というより気づいてきました。
チームでの開発がしたいと思っていましたが、それは漠然と同じ会社の人と としか思っていなかったのが考え足らずだったのだな と。

チーム内でプログラマの仲間がいれば、相談できるし、なにかと安心 というのは確かにあって、それを期待していた自分もいました。また、チームは遠隔地ではなく一緒の場所で作業を行いたいという思いも強かった。(前職はずっとそうでしたので)

ですが、小さい会社で請け負う小さい案件では、複数人のプログラマが携わることは少なくなります。
時代の流れ的も短納期、少金額 で行う案件が増えてきており、要件が固まっていないのに期間が少ないという傾向があるので、一人の方がやりやすい場面も多い。
逆に、一人で2,3個並行してガシガシやっていかないとツライ時代ですよね。

とわいえ、大きいお仕事はあまり興味が湧かない。
とわいえ、一人で仕事するのは寂しい。

では、どんなチームだとよいのか? と考えたとき、
違う能力、違う目線を持つ人同士でのチーム開発である方がワクワク楽しいのではないかと。
特に小さい規模では、能力の種類差は大きければ大きいほど効率が良いのではないかと。

ヌーラボでは「コラボレーションカンファレンス」というイベントを数回開催していて、その際に "コラボレーションとは?"という議題のワールドカフェを行ったのですが、コラボレーションという言葉でイメージするものは、「全く違う会社や業種の人が集まって、+α のものを作り出す」という話が良く出てきました。そんな感覚のチームって楽しそうだなぁと。

大きい案件の場合、DBに強い人、サーブレット層に強い人、UI層に強い人... とか特徴のある人同士 かつ お互いが尊敬しあっている と楽しくお仕事できたりしますよね。
それが小さい案件の場合は、もっとカテゴリが大きく括られるのだろうと思います。

そんな中... PLACES(WEBデザイン会社)からお声を頂いて、転職を決意しました。
今まで、システム開発会社にばかりいたので、WEBデザイン会社は正直考えた事は無かったのですが、うるうる考えていた思いとも一致するし、長年プログラムを書いているのでプログラマ一人の状況でもやっていける...というかやっていかなあかんやろう と。
また、チャレンジしていきたい方向性が一緒だという事、PLACESの社長さんとは似ている所が多いなぁ と以前から思っていた事 も要因として大きいです。

PLACESは入社当時にはヌーラボと同じ事務所内にあったので、当初から仲良くさせて頂いていました。
ヌーラボに入社しない限り、PLACESに入社というのはあり得なかっただろうなと思います。
そう思うと、不思議なご縁だなーと思います。
ヌーラボ入社前にたまたま行ってみたCSSniteに登壇していたPLACES社長が、事務所に現れてびっくりした のはいい思い出です。

今後は、
7月中はぷーたろう した後、8月からPLACESに入社予定です。

ヌーラボ在職期間は2年半でした。
短い間でしたが、大変お世話になりました。
• • •

2011年7月10日日曜日

iPhoneアプリ開発-画面遷移のアニメーション

前回、設定画面ではフリップのアニメーションで画面遷移することが多いみたい と書きましたが、
実際どんなことが標準で用意されているのか?をまとめてみたいと思います。

画面遷移で行える以下のパターンを説明します。
1. 子階層のレベルの画面に遷移
2. 兄弟階層のレベルの画面に遷移
3. 全く違う画面に遷移(設定画面など)

1. 子階層レベルの画面に遷移

子階層に移動するには UINavigationController を使って遷移していきます。
UINavigationControllerの標準の動きは "右から左の方向へ push" することにより遷移していきます。

実際のコードはこれ。
[self.navigationController pushViewController:myController animated:YES];

これを使うと、NavigationBar (画面上部のヘッダ部分) が引き継がれ、
左のボタンは、前画面へ戻るボタンが自動的に追加されます。
ボタン名称は、前画面の画面タイトル(Navigationのタイトル)になります。

子階層の画面に遷移するときは、これ以外の動きのパターンは基本使わないかな..と思います。
が、スライド動作で、親画面にもどる 動作をコードで追加したい場合とか自分で組み込みたくなるときがあります。
そんな場合は、私はCATransitionを使っています。

CATransition *transition = [CATransition animation];
transition.duration = 0.4;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionPush;
transition.subtype = kCATransitionFromLeft;
[self.navigationController.view.layer addAnimation:transition forKey:nil];
[self.navigationController pushViewController:setting animated:YES];

上記の例は "左から右へ push" です。
設定は type と subtype があり、
typeは
kCATransitionFade
kCATransitionMoveIn
kCATransitionPush
kCATransitionReveal
subtypeは
kCATransitionFromRight
kCATransitionFromLeft
kCATransitionFromTop
kCATransitionFromBottom
があります。組み合わせでかなり細かく設定できますね。

※ただ、これ以外にもっと楽な方法が有りそうな気もしなくもない... もっとよい方法があれば教えてください!
※CATransition ですが QuartzCore に入っているので、ライブラリを設定し、#import でインポートしておく必要があります。

2. 兄弟階層のレベルの画面に遷移

この場合は、コンテンツの内容により、画面遷移のアニメーションは変わる可能性があります。
前画面が一覧画面で、現画面がその詳細画面 という場合には、
前のコンテンツへの移動は 上から下へのpush
次のコンテンツへの移動は 下から上へのpush
が自然な気がします。

また、本の1ページのイメージの画面では
前のページへの移動は 左から右へのpush
前のページへの移動は 右から左へのpush
が自然ですね。

このパターンの場合、画面のレイアウトは同じで、中身のコンテンツ(文字列とか)を再描画し直すというパターンが多いかと思います。
そんなときは画面遷移とは言わないかもしれませんがw

Viewを単純に切り替えたい場合は、アニメーションは独自でゴリゴリ書くときもあるとおもいますが、表示中のViewに対して addSubView してあげる感じでしょうか。
その場合のアニメーションの指定は、前述のCATransitionが使えます。

CATransition *transition = [CATransition animation];
transition.duration = 0.4;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionPush;
transition.subtype = kCATransitionFromLeft;
[self.navigationController.view.layer addAnimation:transition forKey:nil];
[self.view addSubview:myController.view];

この場合、NavigationBarは同じものが表示されます。

3. 全く違う画面に遷移(設定画面など)

設定画面などは現在表示しているコンテンツとは全く違う情報を表示することになります。
どんなパターンがあるか...はアプリによりますが、設定画面のようなものと仮定すると、モーダル画面として使う事になるのがほとんどなのでそのパターンで説明します。

その場合は、NavigationController の
[self.navigationController presentModalViewController:myController animated:YES];
を使用します。
標準の動きは "下から上へのpush" です。
このアニメーションを変えるには、このコードの前に遷移先のViewControllerに対して modalTransitionStyle を設定します。
modalTransitionStyle は 以下の種類があります。

UIModalTransitionStyleCoverVertical : 下から上へのpush
UIModalTransitionStyleFlipHorizontal : フリップ (画面がくるっと回りながら遷移)
UIModalTransitionStyleCrossDissolve : ディゾルブ (元画面がだんだん薄くなって先画面がだんだん濃くなる)
UIModalTransitionStylePartialCurl : ページカール ※v3.2以上 (マップアプリの右下のボタンの動き)

画面遷移した後、元画面に戻る場合は、遷移先画面で
[self dismissModalViewControllerAnimated:YES];
を行います。
戻る場合にも同じようなアニメーションをかけてくれます。かしこい。

presentModalViewController を使う場合は、元々表示されていたNavigationBarは表示されなくなります。
全く違う画面が表示されるから...ですね。

でもモーダル画面でもヘッダ(NavigationBar)は表示したいというときがあります。
そのときはNavigationBarを新たに作って設定してあげます。
遷移先画面のレイアウト時に行うのではなく、NavigationControllerを作って、そのControllerで遷移をする方がスマートかと思います。

UINavigationController* navigation = [[UINavigationController alloc] initWithRootViewController:myController];
[self.navigationController presentModalViewController:navigation animated:YES];

こんな感じ。


ながながと書いてしまった。。
• • •

2011年6月27日月曜日

スマートフォンUI考察-設定ボタンの場所

アプリケーションには設定画面が付き物。
Androidの場合は、メニューボタンを押して出てくるメニューで設定画面へのボタンを用意できるのでさほど困らないのかな。
iPhoneの場合はちょっと悩みの種です。

iPhoneでは、
 1. アプリ内で設定画面を用意する
 2. 端末の設定にて設定画面を用意する
の2種類が考えられます。

2.端末の設定画面 はアプリ外の画面になるので見落としがちになります。
そのため、登録が必須なもの..アカウント設定など..はアプリ内で作る事が多く、本当にオプション的なもののみ 端末の設定で用意する事が多いようです。

では、アプリ内で設定画面を表示するボタンは、どこに配置するのがよいのでしょうか??
標準アプリではアプリ内の設定画面を用意しているものは少ないので、有名どころのアプリをみてみます。

twitter
アカウント設定画面(Root画面)の フッダ(ToolBar)左 の [設定] ボタン

facebook
メニュー画面(Root画面)の ヘッダ左 を押すとActionMenuが表示されその中に各種設定へのリンクボタン

Skype
フッダ(TabBar) の 一番右 の [アカウント情報] のタブ

foursquare
フッダ(TabBar) の 一番右 の {アカウント名}のタブ

Evernote
全てのノート画面(Root画面)の ヘッダ左 の [設定]ボタン
(ヘッダ右にもボタンあり)

Dropbox
フッダ(TabBar) の一番右 の [設定] のタブ

Instagram
フッダ(TabBar) の一番右 の {アカウント名} のタブ

Google Latitude
フッダ(TabBar) の一番右 の [設定] のタブ

ロケタッチ
フッダ(TabBar) の一番右 の [ステータス]タブ 画面の ヘッダ右 のボタン (アイコンのみ)

beluga
Pods画面(Root画面)のヘッダ左 の ボタン (アイコンのみ)
(ヘッダ右にもボタンあり)

bump
メニュー画面(Root画面)のヘッダ右 のボタン(アイコンのみ)
(ヘッダ左にはボタンなし)

byline
フォルダ画面(Root画面)のヘッダ右 の[設定]ボタン
(ヘッダ左にはボタンなし)

※ Root画面と書いているのは、初期表示画面ではなくて、ナビゲーションの最上位に位置する画面をさしています。

ずらずらと書きましたが、まとめると

TabBarを使用している画面は、
タブの一番右のボタンに配置

TabBarを使用せず、ヘッダがNavigationタイプになっている画面は、
ヘッダ左右共にボタンがあるときは 左に配置
ヘッダに1つだけボタンがあるときは 右に配置

が多いようです。

みなみに、ヘッダがNavigationタイプでの設定画面への画面遷移(Transition)は
twitter フリップ
Evernote フリップ
beluga 下からスライド
bump フリップ
byline 下からスライド
でした。
思ったよりフリップ多いなーとの印象。
• • •

2011年6月25日土曜日

スマートフォンUI考察-iPhone標準レイアウト

スマートフォン向けのアプリを作ろうとするとき、アプリを見直して、何がどこにあるのが一般的かを毎回ぼーっとみています。
忘れてしまうぐらいなら記事にしてしまえ! ということで、スマートフォンUI考察 シリーズ と題して書いてみようと思います。

第一回は iPhone標準レイアウト

HTMLではこんな配置が多いかと思います。

iPhone, Androidなどスマートフォンでは、解像度は高くなったとはいえ、画面サイズが小さいので全て1画面に収めるのには無理があります。
必要なの要素を、どれだけ省略してすっきり画面内に収めるか。というのが腕の見せ所(?)かと思います。
なにはともあれ、iPhoneの標準アプリがとっても参考になります。

時計

時計アプリには時計の他にストップウォッチなど、複数の機能があります。
それらの機能はグロバールナビゲーション的なタブ切替として一番下に配置されています。
iPhoneで"タブ"と呼ばれるもの(UITabbedPane)は、下に表示されるのが一般的です。

この画面のレイアウトは、登録系画面の基本的な形になるかと思います。

AppStore, Youtube

フッダ部分にタブ形式でグローバルナビゲーション。
ヘッダ部分に表示切替のトグルボタン(SegmentedControll)が配置されています。
タブで表示内容を決め、トグルボタンでさらにフィルタをかけるイメージです。

この画面のレイアウトは、一覧表示系の画面にて使用される形になります。
登録系画面では使用されないはず。

カレンダー

タブがないパターン。
追加ボタンは、通常 ヘッダの右に配置される事が多いです。
この画面では追加ボタンがあるため、表示切替のトグルボタンは、フッダに配置されています。
たまに、ヘッダに 追加ボタンと表示切替のトグルボタンの両方がヘッダに配置されているアプリもあります。

カレンダーの種類を設定する画面へのボタンがヘッダ左に配置されています。
階層的に親画面になるものは、ヘッダ左に遷移ボタンが配置されます。
遷移元画面へ戻るボタンで、 <□□ の形のものがありますが、これは実際遷移を行ったときに表示されるものです。 カレンダーアプリではよく使うこの画面をデフォルトの画面にしているため、親画面を経由していません。 そのため □ の形状のボタンになっているのと思います。


Map


検索バーは通常画面上部に表示されます。
ヘッダ + 検索バーが表示されるレイアウトもありますが、コンテンツ表示可能サイズが小さくなってしまうので、その場合は検索バーは通常は非表示にする事が多いと思います。

Safari

Mapと似た配置になります。
ページタイトルは、検索バーの中に書くことでサイズを節約できています。

標準アプリはUIの統一感がとれているとおもいませんか?
自然に使っているけど、よく考えられているんだろうなーと思います。

標準アプリ以外の有名どころのアプリも少し見てみます。

Facebook


処理ボタンがヘッダの下にあります。
ちょっと標準形式から崩れているので、何をするボタンだろう? 表示切替かな? と始めに思ったりしなかったでしょうか?w

foursquare


標準形式にまだ似ているので、分かりやすいんじゃないかと思います。
ただ、
横の 周辺のスポット 、世界中 のトグルボタンは、この画面内の表示切替なのに、マップ表示の画面に飛ぶ処理ボタン(地球儀のマーク)がその横にあるのは分かりにくいなぁーと思います。

画面が少ないから、いろんな機能、ボタンを収めてしまいたい!と思うのは良くわかるのですが、
横に並んでいるものは、画面遷移をするもの、画面内の情報を切り替えるもの は混在させない方が分かりやすいかと思います。

今回はこんなところで。
次回も続く予定。たぶん。
• • •

2011年6月8日水曜日

MacOSXのchromeでスマートフォン用UserAgentを偽装

MacOSXのchromeでスマートフォン用のUserAgentを偽装して表示したかったので、User-Agent Switcher のExtensionを入れてみたのですがなんか上手く動かず、イラっとして起動用スクリプト書いてみました。
UserAgentの値はMAX FACTORY スマートフォン機種情報一覧から頂きました。有難うございます。
起動アプリはgmailになってます。(URLの変数の値を変えると変更できます)
終了時にchromeのエラーが出力されたりするのですが...気にせず使ってますが...なにか間違っていたらすいません。

#!/bin/sh

# 指定されたUserAgentをつかってGoogleChromeを起動します。@ Mac
# 
# chromeの設定は ${USER_DATA_DIR}の場所に入ります。

USER_DATA_DIR="/~/Library/Application Support/Google/Chrome/smartphonedev"
URL="https://mail.google.com/mail/"

# UserAgent一覧
iPhone4="Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_0 like Mac OS X; en-us) AppleWebKit/532.9 (KHTML, like Gecko) Version/4.0.5 Mobile/8A293 Safari/6531.22.7"
iPhone3GS="Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_1_3 like Mac OS X; ja-jp) AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0 Mobile/7E18 Safari/528.16"
iPad="Mozilla/5.0 (iPad; U; CPU OS 4_3_3 like Mac OS X; ja-jp) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5"

HT03A="Mozilla/5.0 (Linux; U; Android 1.5; ja-jp; HT-03A Build/CDB72) AppleWebKit/528.5+ (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1"
Xperia="Mozilla/5.0 (Linux; U; Android 1.6; ja-jp; SonyEricssonSO-01B Build/R1EA018) AppleWebKit/528.5+ (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1"
GalaxyS="Mozilla/5.0 (Linux; U; Android 2.2; ja-jp; SC-02B Build/FROYO) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1"
IS01="Mozilla/5.0 (Linux; U; Android 1.6; ja-jp; IS01 Build/S6191) AppleWebKit/528.5+ (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1"
IS02="Mozilla/4.0 (compatible; MSIE 6.0; Windows CE; IEMobile 8.12; MSIEMobile 6.5) KDDI-TS01"
IS03="Mozilla/5.0 (Linux; U; Android 2.1-update1; ja-jp; IS03 Build/S2080) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17"
Desire="Mozilla/5.0 (Linux; U; Android 2.1-update1; ja-jp; HTCX06HT Build/ERE27) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17"
Aria="Mozilla/5.0 (Linux; U; Android 2.2.1; ja-jp; S31HT Build/FRG83) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1"

while true; do
    cat >>EOF
使用するUserAgentを選択してください
1:iPhone4
2:iPhone3GS
3:iPad
4:HT-03A
5:Xperia
6:GalaxyS
7:IS01
8:IS02
9:IS03
10:Desire
11:Aria
数字を入力してください
EOF
    read ACT
    case ${ACT} in
     1)  USER_AGENT=$iPhone4 break ;;
     2)  USER_AGENT=$iPhone3GS break ;;
     3)  USER_AGENT=$iPad break ;;
     4)  USER_AGENT=$HT03A break ;;
     5)  USER_AGENT=$Xperia break ;;
     6)  USER_AGENT=$GalaxyS break ;;
     7)  USER_AGENT=$IS01 break ;;
     8)  USER_AGENT=$IS02 break ;;
     9)  USER_AGENT=$IS03 break ;;
     10)  USER_AGENT=$Desire break ;;
     11)  USER_AGENT=$Aria break ;;
     *)
      printf "エラー: 無効な項目 \"%s\" が入力されました\n" "${ACT}"
      ;;
    esac
  done
 
echo userAgent = "${USER_AGENT}"

/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --user-data-dir="${USER_DATA_DIR}" --app="${URL}" --user-agent="${USER_AGENT}"
exit 0


user-data-dir を指定するとか知りませんでした。
開発テストではdirを切り分けるとなにかと良いかも。
• • •

2011年6月7日火曜日

UITabBarControllerのloadViewのタイミング

iOS の UITabBarController でちょっとハマったのでメモ。

UIViewController と UITabBarController とでは loadView, viewDidLoaded のイベントのタイミングが違うみたい。
@interface MyViewController : UIViewController {
}
@end

@implementation MyViewController
- (id) init {
    NSLog(@"super init call!");
    self = [super init];
    if (self) {
        NSLog(@"init call!");
   }
   return self;
}

- (void)loadView {
    [super loadView];
    NSLog(@"loadView call!");
}

- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"viewDidLoad call!");
}
@end

こんな感じのUIViewController の場合、
1. super init call!
2. init call!
3. loadView call!
4. viewDidLoad call!
の順番で呼ばれます。

... が 継承元を UITabBarController にした場合、つまり上記のコードでinterface部分を
@interface MyViewController : UITabBarController {
}
@end
とした場合は、
1. super init call!
2. loadView call!
3. viewDidLoad call!
4. init call!
の順番で呼ばれるみたい。

なので、UITabBarController の init の所でプロパティの初期化をするようなコードを書いいると、
loadView のタイミングでは初期化されていない という状態になってしまいます。

Tabだからそういうもんかもしれないかもしれないけど、おかしい気もする。うーん ?

私的には
UITabBarControllerを継承してクラスは作成しない方向でこれからは組む
...と心に決めましたw
• • •

2011年4月30日土曜日

スマートフォン向けアプリ開発フレームワークとか

スマートフォン向けのアプリ提供はほぼ必須になってきているこの状況。
何で作るのか? というのは、悩ましいところですよね。

ネイティブアプリで作るのか? WEBアプリ(HTML5+CSS3)で作るのか?
...は開発者が揃っている云々もあるかもしれませんが、それよりも、そのアプリの特性/対象者によって決定する感じなのでしょうね。
会社のアプリならばインストールさせて使う系のネイティブの方が良さそうだけど、インストール無しでも..であればWEBアプリでという選択肢なのかな。あとは、Appleにお布施が可能なのかどうかとか..か。

WEBアプリの方には全然注目していなかったのですが、「ネイティブと同じような動きで、iPhoneとAndroidのマルチプラットフォーム」となるとなかなか厄介なもんですね。
どこまでこだわるか?によるんでしょうけども、対象が iPhoneだけだったらライブラリつかってなんとかなりそうだがAndroidも加わるんだったらイバラの道 というのがなんとなくの現在のイメージ。
まだ本格的に作った訳じゃないから、違うかもしんないけど。
そもそもAndroidはアプリのメニューや戻るがハードボタンなので、ネイティブアプリに見せるWEBアプリは果たしてどうなんだろう? 混乱しない? とも思わくもないのですがね。

ということで、JSで作成出来る便利ライブラリ系を最近探してみたのでいくつか書いてみます。

Sencha Touch

http://www.extjs.co.jp/products/touch/
ExtJSをベースに作成されているので、ExtJSに慣れている人なら学習コストは低そう。
HTMLは書かずにほぼJSでゴリゴリ書いていきます。
好き嫌いが別れるところですが、個人的にはHTMLで要素構造が書けないのでメンテが難しそうなイメージも。
ライセンス形態が変更になって、商用でも無料で使えるようになりました。
最終生成物はWEBアプリ。

jQueryMobile

http://jquerymobile.com/
HTMLでマークアップ+JQueryで書いていくタイプ。
ボタンとかのデザインはCSSの指定クラスを書いてその情報を元にJQueryMobileがHTMLを修正することにより反映していきます。
そのため、動的に追加したHTML要素については作成後に反映するrefresh的なJSメソッドをcallしたりする必要も有ります。
SenchaTouchと良く比較されているものを見ますが、個人的にはSenchaTouchより敷居が低い気がします。
ただ、GalaxySで見てみると画面遷移がカクついたりといまいち。
ヘッダ/フッダ固定機能も有りますが、スクロール後にヘッダ/フッダを描画しなおす感じもののです。その動きもなんだか不安定で表示されなくなったりも...

SproutCore

http://www.sproutcore.com/
AppleのMobileMeで使われているようです。
デモサイトを見てみたんですがなんかまともに動かなくて..

http://demo.sproutcore.com/sample_controls/
これを見ると、iPhoneっぽいきれいなUIが作れるコンポーネントが揃っているようです。
デザインは不得意な人にはよいかもしれません。
ライセンスはMIT license。

qooxdoo

http://qooxdoo.org/
qooxdoo1.4 が2011/4/7にリリースされて、モバイル対応も追加されたようです。
実験的に追加されたものらしく、iPhoneではデモサイトがそれなりにう動いてそうでしたが、GaralxySではヘッダ固定画面のスクロールはいまいちな動き。画面遷移のアニメーションも動かないところがいくつかありました。
でも、ヘッダ固定とか画面遷移のアニメーションとか欲しくなりそうなところを押さえてようとしている感じなので今後に期待。
ライセンスはLGPLとEPLのデュアル。

PhoneGap

http://www.phonegap.com/
JSで書いたものWebkitで動かすタイプだが、ネイティブアプリのパッケージになる。
開発環境も、iPhoneだったらXCodeらしい。
ObjectiveCの学習コストがかかるから...という人が選択するもんだと想像。
Rejectされる可能性もある?! と噂されていましたが、結局は大丈夫と落ち着いたみたい?
個人的には、フレームワークの使い方の学習コストを考えるなら、開発者だったらObjectiveCの開発コストの方が低いんじゃねの? なんても思ったり。
でも、PhoneGapのページかわいい。好き。
MITライセンス。

Titanium Mobile

http://www.appcelerator.com/products/titanium-mobile-application-development/
JSで書いたコードをネィティブコードにジェネレートするタイプ。 MASUIさんのスライドを見るとネイティブコードにジェネレートではないらしいです。 ですが、他のライブラリに比べてより多くのネイティブの機能を使えるのが特徴なんかと思います。書くJSコードもネイティブのクラスをJSのラッパーで触るのに近いイメージ。Androidだったらインテントを発行とかもできるらしい。

mogsnapはTitaniumで作成されているようです。
なんかあつくなってきている感じですよねw
PhoneGapと同様、Rejectされる可能性もある?! と噂されていましたが、リリースされているアプリもあるし大丈夫って事なんでしょうー。
ジェネレート系なので、レスポンスもWEBアプリに比べていいだろうし、ネイティブのAPIが必要になっても最悪書いちゃえばOKという柔軟さもある。
ヘッダ固定とか、画面遷移のアニメーションとか をそれらしくすれば、それなりにネイティブアプリに近くなると思うのですが、WEBアプリ(HTML5+CSS3)でクロスプラットフォームでやるにはやっぱり大変。
本当に真面目にやるんであれば、iPhone用、Android用にソース分けた方が結局よい..とかなりそうな予感もするので、それだったらTitaniumのようなジェネレートタイプって結構現実的な気もします。
まぁ、やっぱりネックなのはiPhone用を作成するにはXCodeとか、開発用ライセンスが必要って事なんですけどもねー。
価格体系 → http://www.appcelerator.com/products/plans-pricing/

jQTouch

http://jqtouch.com/
iPhoneのwebkitに最適化されているjQueryのプラグイン。
MITライセンス。

http://www.extjs.co.jp/blog/2010/06/15/sencha-press-release/
これをみると、Senchaと一緒になったの?

iScroll

http://cubiq.org/iscroll
スクロール用のライブラリ。
スマートフォン向けのWEBアプリはスクロールはネックの一つ。
ググると結構使われているみたいなんですけど、GalaxySでスクロールを見るとスクロールが動かなくなったりして動きが不安定。
使われているのはiPhone用のものなんかなぁー。
MITライセンス。

WINK Toolkit

http://www.winktoolkit.org/
かなりエフェクト系が多いです。こんなんまでいらんやろーw と思うくらい。
いっぱいdemoが用意されていますが、GalaxySでみるとFlipPageやCoverflowなど対応していないようでメニューが使用不可になっています。こちらもiPhoneメインの開発なんでしょうね。
ヘッダ固定のスクロールについてはiScrollと同じくGalaxySではちょっと怪しい動きもあるのは残念。
だけど、いろいろ参考になりそうです。
ライセンス : http://www.winktoolkit.org/download/licence.txt

mobl

https://github.com/mobl/mobl
moblという独自言語を使って開発するフレームワーク。
開発環境は魅力的なEclipse (pluginが提供される)
まだ触っていないけど、どうなんだろ?

iUI

http://code.google.com/p/iui/
iPhone用のUIが作れるJavaScriptライブラリ。
デモ : http://www.iui-js.org/mobile/demos.html#_home
New BSD ライセンス。

2011/05/02 追加

Jo

http://www.joapp.com/
IBM DevelperWorks/Jo と PhoneGap を使用してネイティブ・モバイル・アプリケーションを作成する
記事を読むかぎり、JSをWebkit上で動かすタイプなのかな。
画面管理や、データのバインドなどがあるみたいなので、デザインよりもデータ管理の方面に強いフレームワークなのかな。

appMobi

http://www.appmobi.com/index.php
MoonGift:Web上でiPhone/iPad/Androidアプリを開発する「appMobi」
• • •

2011年4月26日火曜日

JQueryのliveイベントがiPhoneのSafariで登録できない

JQueryのliveで登録したイベントがiPhoneのSafariでは動作しなくて暫くのハマり。

$('#identifier').live("click", function);

こんな感じの登録のやつです。
もち、PC上のブラウザでは動く。(多分Androidのブラウザも動いてた気がする)

なぜかiScrollに、対象のHTML要素を含んだものを入れるとイベント登録ができたりして、よけいに混乱しておりました。(理由不明)

JQueryの該当チケットは多分コレ。
Ticket #5677
LIVE CLICK EVENTS DON'T REGISTER ON MOBILESAFARI (IPHONE)
ここのコメントにありますが、回避方法は
onclick="" 属性を対象HTML要素に追加
です。

これで無事に動くようになりました。それはそれで びっくりですけども。
• • •

2011年4月1日金曜日

NSAutoreleasePoolとNSValue

さっき、ハマっていたのですが、あまりに初心者的なハマりだったので、
テストケースとしてコミットするのをやめ、ブログにメモとして残すことにしました (笑)

[NSValue valueWithCGPoint:CGPointMake(10, 10)]

[NSDate date]
のようなallocしてなくてinit***から始まらないものは、Autoreleaseで解放されちゃうので注意 ということ。

NSAutoreleasePoolなんて、つかってねーよ! という場合でも使用ライブラリによっては実は走っていた!なんて事があるのでちゃんとしとかなくてはイケマセン。

もれちゃうと、EXEC_BAD_ACCESS がでちゃったり、メモリーリークしちゃったり、このタイミングではOKなのに、その後のタイミングではNGだ ... と私のようにハマりますw

以下、簡潔にしたテストコード。
- (void)testNSValue {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    NSValue *nsValue = [NSValue valueWithCGPoint:CGPointMake(10, 10)];
    [nsValue retain];
    STAssertEquals((int) [nsValue retainCount], 2, @"retain したので2つになっているはず");
    
    NSObject *nsObject = [[NSObject alloc] init];
    [nsObject retain];
    STAssertEquals((int) [nsObject retainCount], 2, @"retain したので2つになっているはず");
    
    NSDate *nsDate = [NSDate date];
    [nsDate retain];
    STAssertEquals((int) [nsDate retainCount], 2, @"retain したので2つになっているはず");

    [pool release]; // autorelease を release
    
    STAssertEquals((int) [nsValue retainCount], 1, @"autorelease により1になる");
    STAssertEquals((int) [nsObject retainCount], 2, @"NSObjectはallocしてるのでautoreleaseしても2のまま");
    STAssertEquals((int) [nsDate retainCount], 1, @"autorelease により1になる");
}
• • •

2011年3月21日月曜日

東日本大震災 10日目

大地震、大津波から10日がたちました。

被害が露になっていく一方、奇跡的なニュースも飛びかう日々。
ニュースで出てくる方々の表情を見ていると、お年寄りたちは強いなぁと感じます。
戦争や、かつての災害を経験しているからでしょうか。

私の周りの東京では、大分食料も落ち着いてきて、その日のものはその日に買えるようになってきました。
我が家は23区にあるので停電はありませんが、スーパーは18時には閉店するし、飲食店の看板はほとんど消されているために、どの店が空いているのかは、目の前まで行かないと判断できない感じです。

気になるところとしては、働いて、夜帰ってくる人が食料/生活用品にありつけないということ。
先週は自宅勤務だったので様子がよく見えたのですが、朝はちゃんと入荷があるんですよね。そこに、家にずっといてそうな方々がまとめ買いをしていくから、夜にはなくなる。
働いている人の事も考えて、買いだめの自粛してほしい...。

相変わらず、ティッシュ類は買えませんが、節約して使っていると、いままで凄く浪費していたんだなぁということが良くわかります。モノがあふれていて鈍っていたのだろう。

1日の電力消費量が発表されるので、予定より少なかった結果だと嬉しくなりますね。
この震災時だけでなく、常に数値を出してくれたらよい気も。
計画的停電はこれからもしばらく続きそうなので、これを機に節電が習慣づいたらよさそう。

まぁ、今、被災地に対して出来る事といえば、募金と節電/節約 なのでこの調子で東京都民も頑張っていきたい!

原発のニュースが、当初に比べて少なくなってきたのが気がかり。
情報を提供しすぎて整理できない視聴者が増えたから自粛したのか? 視聴者が飽きたと判断されたのか?

ネット環境がある私としては、具体的な数値や、何の機器が壊れていて計測できないのか の事実だけでもいいのでタンタンと報告してほしいと思う。

追記:公開されているデータ
文部科学省 全国の放射線モニタリング状況 http://www.mext.go.jp/
東京電力 福島第一・第二原子力発電所モニタリングによる計測状況 http://www.tepco.co.jp/nu/monitoring/index-j.html

政府が事実を隠蔽してしまう というのは世界中見ても常に発生しうる事であって、基本的にはみんな心からは信用していないと思う。
「大丈夫」とか「大丈夫でない」というのも、判断する人によって変わるだろうし、受け取り側の立場によっても違う。
まぁ、数値まで手を加えられていたら判断しようがないですけど、常に報告している数字が突然変わったり、報告がなくなったりする状態になれば、注意深く見ないとという判断ができる。

家に帰ると、最新ニュースはないかを確認するためTVをつける日々なのですが、最近、ニュース番組も減ってしまったので、情報が入りにくい。
情報が欲しいときに、ピンポイントの情報が入りにくいというのはTVのデメリット。

こういう事って、原発に限らず普段でも一緒ですね。
例えば、仕事についても。現場の人ではなく、プロジェクト管理者やエンドユーザーから見た観点と同じ感じ。

来週から通常出勤になる会社も多いかと思うので、また電力が足りなくなったための、突発的な停電、電車本数の減少が有ると思いますので、無理せず余裕のある生活をおくりましょう。
• • •

2011年3月14日月曜日

東日本大震災

阪神・淡路大震災の時は大阪にいて、今回は東京にいる...という、被災地とは呼べないが、部屋の中が散らかったぐらいの位置に暮らしています。
被災者の方々が少しでも早くに安心して暮らせるように祈るのみです。

東京の状況はニュースにはでないと思うので、思い出すかぎり淡々と書いてみようかと思います。

大きく揺れたがどれくらいの規模なのかが分からない。事務所にはテレビがない。
NHKがUSTしてくれたおかげで惨状を知る。
電話は全く通じないが、ネットは大丈夫。twitterも大丈夫。有り難い。
電車は無理だと判断して歩く決心をする。
16:30会社を出る。

交通網が崩れると大変な事になる東京。
まともに歩けないぐらいの人の多さがずっと家まで続く。
家の前でさえ、集団でひたすら歩く人達の波&車の渋滞は夜半過ぎまで続いていた。
自転車屋さんには自転車を買う人々で長蛇の列ができていた。かしこい。
こんな東京見た事ない....

コンビニではまとめ買いをする人々。食料品の商品棚は18時くらいでほぼ無くなる。
家には、帰れなかった人達が集まり、ひたすらテレビを見る。

電車の復旧は思ったより早く、終電時間ぐらいでポツポツ復旧した電車が出てきた。
その電車に並ぶ人達の数は数百人を超え、東京にはこんなに人がいるのだと改めて実感。
そんな状況なのに整然と並ぶ人達。譲り合う人達。
徹夜で復旧をし稼働して頂いた鉄道インフラの方々に感謝。

24時間コンビニ、スーパーは、早々に閉まっていた。
売れる食料がもう無くなったためだろう。。

なか卯は、食料はないがお茶は出せる。休憩にどうぞ の張り紙が。
帰宅困難者に向けて、ホテルなどが場所を提供している。
心温まる。

翌日
朝10時にスーパーの開店と同時に行ってみる。
地震当日は早々に閉まってしまったスーパーだったため、豊富に食料があるのに安心。
安心しすぎて買いだめをしなかった。(後で後悔)

テレビのニュースに釘付け。ニュースみていないと落ち着かない。
なにも作業をしない日になった

翌日
お店は空いている店があるが、品薄の張り紙が多い。
スーパーは品切れ多発。

被災地優先にして欲しいからしょうがないと思うのだが、お米が全然売っていないのを見るとなんだか焦った。やっぱり私は日本人。
しょうがないので、少し残っていたパスタを購入。

なにが起こるか分からないので、お水を貯めた。

明日は計画停電があるらしい。
私の家は停電地区に含まれていなかったが、突然決まった事なので何がおこるか分からない。
停電になったら、何が使えないかを考えていたら、気づかず電気に頼っているのが良く分かる。

まだまだ余震も続く。
明日はどうなるのだろう。せめて晴れていてほしい。
• • •

2011年3月3日木曜日

空うさぎ 私はこんな設定をしています-フィルタ編

要望があったのでエントリ。

色々設定項目がある空うさぎ
設定内容も人によって千差万別になると思いますが、私の設定をご紹介します。

まずフィルタ。


あまり多くないですね、私。

1番上の最優先フィルタは、StreamingAPIで取得したもののフィルタ。
今回Streamingと言っているのは、Twitterのリアルタイム検索で、
設定 > twitter にて、 [Streaming] のチェックボックスをONでメイン画面上部のテキストフィールドに文字を入れて右横のボタンを押す事で表示ができます。
これを使うのは、なにかのイベントの時に、ハッシュタグで関連ツイートを検索する時。
大量にツイートが流れてくる事が多いので、通知はON , リストには表示しない ようにして、通知だけで楽しんでいます。

2,3,4番目のTwitter系フィルタは特別なことをしていないので飛ばして...

5番目の空うさぎフィルタ。
空うさぎについてのエントリを抜き出すフィルタです。
私は「空うさぎ」という言葉に注目したいのでこのフィルタを作っています。
フィルタ自体は [文字一致] の条件に、空うさぎ|空ウサギ|#sorausagi|#sora_usagi を設定しているぐらい。
ただ、Twitterのタイムラインで取得できなかったものも引っかかるようにRSSで空うさぎ情報を取得できるようにしています。
Twitter検索で#sora_usagiを引っ掛けているRSSの設定
他にはFriendFeedの検索でも空うさぎの文字を引っ掛けているRSSがあったりします。
URLはこんなかんじ。http://friendfeed.com/search?q=空うさぎ&format=atom

TwitterやRSS...と、違うデータソースのものでも1つのフィルタで引っ掛けるやり方は、空うさぎ っぽい感じですw。

6番目のFamilyは家族用のフィルタなので飛ばして..

7番目の ぬーらぼ は会社の人用のフィルタです。
Twitterのリストがあるので、[データソース]でそのリストを選択でOKなのですが、その設定はしていません。
その理由なのですが...
リストにいる人でもfollowしている人がほとんどなので、FriendTimelineとリストと両方からエントリを取得する事になります。
もし、FriendTimeline情報が先に来た場合はこのフィルタが反応できず、後にリストのエントリが取得された時点でフィルタが反応します。...つまりタイムラグが出てきてしまうので、[ユーザ一致]で名前を書いています。
先日リリースした、Listからのコピーを使いつつ...※1

8番目の Lingr は先日リリースしたLingr用のフィルタ。
設定内容は @shin1ogawa が 空うさぎが Lingr にも対応してより便利になった #sora_usagi を書いてくれています。ご参考にどうぞ!

9番目の Buzz。いつか日の目は見れるんでしょうかw
Privateなエントリを書くことしか最近ないです。。

10番目の RSS。
RSSで取得したもののフィルタです。
このフィルタの設定はこんな感じ。

[サービス名]の所で登録したRSSが選べるのですが、それは使っていません。
というのも、複数のRSSを同じフィルタで引っ掛けたいからです。
そこで、[データソース名]の指定に RSS と記述し、このフィルタに引っ掛けたいRSSは、設定で[表示サービスの変更]の名前を RSS に設定しています。

11番目の Backlog。
Backlog というBTSで出力されているRSSをフィルタするものです。
これも10番目のRSSと同様、複数のRSSを同じフィルタに引っ掛けたいので、RSSの設定の[表示サービスの変更]の名前を Backlog にしておき、フィルタ設定の[データソース名]の指定に Backlog と記述しています。
Basic認証の設定ができますので、TracやRedmineなどのRSSでも表示できますよ。

このフィルタは仕事用のエントリになるので、
会社のマシンでは通知あり、自宅マシンでは通知なし の設定にしたりしています。

12番目の Remove Tumblr。
FriendFeedから取得するエントリの中にTumblrのものがありますが、Tumblrのエントリは邪魔と思う事が多いので表示されない様にしています。
[リストに表示しない] [通知に表示しない] の両方にチェックを入れています。

FriendFeedではTumblr以外にもFlickrとかDeliciousとかのエントリも入ってくる可能性があります。
それらサービスと一致させるための[データソース名]は、リスト画面の左から2個目のアイコン=サービスのアイコン のツールチップをいれれば大丈夫なはず!

最後の Default。
フィルタの一覧画面上でチェックを外すことにより、上記のフィルタでひっかからないエントリは通知しない ようにしています。

フィルタだけで長いエントリにw
また次回は違う所の設定をかいてみますー。

※1
追加したTwitterListのユーザのコピー機能ですが、Listの1ページ目しかコピーできていないらしい。
全部とって来れるように修正しなくては。
• • •

2011年3月2日水曜日

エアリーシェイプ買いました


去年年末から欲しいと思っていたエアリーシェイプスマートを購入しました。
今回購入したのは脚付きのものですが、脚の無いタイプは安いのもあり品切れ多発。
おかげで、年始年末をこの椅子でまったりくつろぐ計画も断念せざるおえない事にw

この椅子は左右、後ろ、下 からエアバックでぐいぐい押してくれます。
これで骨盤が矯正されるとは思えないけど、まぁ気持ちがいいので良しです。
普通の椅子として座りやすいので、通常は椅子に座って作業...疲れたらエアバックON! という日々。
マッサージチェアを欲しい人にはおすすめしませんが、椅子にマッサージ機能がついている みたいな感覚で購入するならいいんじゃないかとおもいます〜

そのため、机も他の部屋から引っ張りだしてきて、My作業場所を作りました。
これで長時間PCに向かっても、腰が痛くなる事は無くなる...はず!
• • •

2011年2月16日水曜日

iPhoneの画像からEXIF,GPSデータを取得する

iPhone上の画像からEXIF, GPS情報をどのように取得できるのか調べてみました。
EXIFは以前はAPIが提供されていなかったらしく、GoogleCode:iPhone-Exifのライブラリを使ったりしていたみたいですが、iOS4.1からAssetsLibraryというのが増えてそちらからアクセスできるようになったみたいです。

で、EXIF, GPS情報は、どのカメラから撮った写真か? によって取得できる情報量が変わるようです。
注意:すべてのアプリの位置情報サービスをONになっている前提

iPhone標準のカメラから撮った画像をライブラリから取得するとき


ライブラリから取得した場合、UIImagePickerから取得したときの引数のinfoからは取得できないようです。
ALAssetsLibraryからmetadataを抜き出します。

- (void)imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary*)info {
 NSURL *url = [info objectForKey:UIImagePickerControllerReferenceURL];
 ALAssetsLibrary *lib = [[[ALAssetsLibrary alloc] init] autorelease];
[lib assetForURL:url
resultBlock:^(ALAsset *asset){
ALAssetRepresentation *representation = [asset defaultRepresentation];
NSUInteger size = [representation size];
uint8_t *buff = (uint8_t *)malloc(sizeof(uint8_t)*size);
if(buff != nil){
NSError *error = nil;
NSUInteger bytesRead = [representation getBytes:buff fromOffset:0 length:size error:&error];
if (bytesRead && !error) {
NSData *photo = [NSData dataWithBytesNoCopy:buff length:bytesRead freeWhenDone:YES];
NSMutableDictionary *metadata = [self performSelector:@selector(getMetadataFromNSData:) withObject:photo];
NSLog(@"%@", metadata);
}
if (error) {
NSLog(@"error:%@", error);
[error release];
free(buff);
}
}
}
failureBlock:^(NSError *error){
NSLog(@"error:%@", error);
}];
}

これで取得したmetadataには、EXIT, GPS 情報が含まれています。
{
ColorModel = RGB;
DPIHeight = 72;
DPIWidth = 72;
Depth = 8;
Orientation = 1;
PixelHeight = 1936;
PixelWidth = 2592;
"{Exif}" = {
ApertureValue = "2.970854";
ColorSpace = 1;
ComponentsConfiguration = (
1,
2,
3,
0
);
CustomRendered = 2;
DateTimeDigitized = "2011:02:14 17:01:51";
DateTimeOriginal = "2011:02:14 17:01:51";
ExifVersion = (
2,
2,
1
);
ExposureMode = 0;
ExposureProgram = 2;
ExposureTime = "0.05";
FNumber = "2.8";
Flash = 0;
FlashPixVersion = (
1,
0
);
FocalLength = "3.85";
ISOSpeedRatings = (
80
);
MeteringMode = 1;
PixelXDimension = 2592;
PixelYDimension = 1936;
SceneCaptureType = 0;
SensingMethod = 2;
ShutterSpeedValue = "4.322165";
WhiteBalance = 0;
};
"{GPS}" = {
ImgDirection = "142.403";
ImgDirectionRef = T;
Latitude = "35.6635";
LatitudeRef = N;
Longitude = "139.7025";
LongitudeRef = E;
TimeStamp = "08:01:49.65";
};
"{TIFF}" = {
DateTime = "2011:02:14 17:01:51";
Make = Apple;
Model = "iPhone 4";
Orientation = 1;
ResolutionUnit = 2;
Software = "4.2.1";
XResolution = 72;
YResolution = 72;
"_YCbCrPositioning" = 1;
};
}

iPhoneアプリのカメラから撮った画像をライブラリから取得するとき


アプリの作り方に依存しますが、特に情報を付加するコードを書いていないとEXIF, GPSの情報は含まれていません。
instagramで撮った写真には入っていませんでした。
{
ColorModel = RGB;
DPIHeight = 72;
DPIWidth = 72;
Depth = 8;
Orientation = 1;
PixelHeight = 612;
PixelWidth = 612;
"{Exif}" = {
ColorSpace = 1;
ComponentsConfiguration = (
1,
2,
3,
0
);
ExifVersion = (
2,
2,
1
);
FlashPixVersion = (
1,
0
);
PixelXDimension = 612;
PixelYDimension = 612;
SceneCaptureType = 0;
};
"{TIFF}" = {
Orientation = 1;
ResolutionUnit = 2;
XResolution = 72;
YResolution = 72;
"_YCbCrPositioning" = 1;
};
}

カメラを起動して写した画像のとき


UIImagePickerを使って、カメラを起動して写した画像のときは、info にメタ情報が入ってきます。

- (void)imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary*)info {
NSMutableDictionary *metadata = (NSMutableDictionary *)[info objectForKey:UIImagePickerControllerMediaMetadata];
}

この中にはEXIF情報は入っているようですが、GPSの情報は入ってきません。
GPSの情報は自分で埋め込んであげないといけないようです。
(kCGImagePropertyGPSDictionary のキーワードでググると情報ありそう)

写した画像をライブラリに保存するときは、

ALAssetsLibrary *lib = [[ALAssetsLibrary alloc] init];
[lib writeImageToSavedPhotosAlbum:image.CGImage
metadata:metadata
completionBlock:^(NSURL* url, NSError* error){
NSLog(@"Saved: %@<%@>", url, error);
}];

とするとmeta情報付きで保存できる様子。



写真の位置情報って、「つけてほしい」時と、「つけてほしくない」時がある。
自宅の写真はつけてほしくないけど、旅行行ったときのはつけてほしい。
でも写真ごとに切り替えるのも面倒。
位置情報がついているのも認識せずにどこかにUploadしちゃったミスも発生したり....

たしか、iPhotoで取り込んだ写真ってJPGにExportするとExifの情報を外した気がするのですが、そんなミスも考慮してなんだろうか...
んが、写した日時はやっぱり残して置いてほしかった気も。。

写真を扱うアプリをつくるなら、
メタ情報をわかりやすく見せてミスを防げるようなアプリを作らないとなー
• • •

2011年1月21日金曜日

iPhoneアプリ申請までの道のりまとめ

無事にアプリをリリースできたので、申請までの記憶をたどって残しておきます。

アプリの登録

iOS Provisioning Portal で 以下を操作

(1) App IDs を登録
ユニークになるようなIDを決めて登録する。
CrossBacklogの場合は、私が持っている sorausagi.org をつかって
org.sorausagi.CrossBacklog にしました。
前にランダムな英数字が振られて *******.org.sorausagi.CrossBacklog のようなのがApp IDに。

(2) Distribution Provisioning Profiles を登録
App Store用のProfileを作成する
出来上がった Provisioning のファイルをダウンロードして、ダブルクリックでインストール

iTunes Connect で 以下を操作

(3) Manage Users でユーザを登録
(4) Contracts, Tax, and Banking で銀行の口座などを登録
(5)Manage Your Applications でアプリケーションの登録
512*512 のアイコン、サポートURL、説明(英語や日本語)などが必要

ここで iTunes Connectのステータスが Waiting For Upload になるはず

申請用アプリのバイナリ作成

xCode で 以下を操作

(6)info.plist の設定を確認。
バージョン番号とか、Bundlenameとか。
bundlenameはAppIDの前のランダム英数字なしのものでよいらしいので、
私のアプリは、org.sorausagi.CrossBacklog で登録。

(7)XCodeでビルドの構成でreleaseを作成
releaseのビルド構成のコード署名を、AppStore申請用のDistribution Provisioning Profiles に設定
(8)XCodeでClean
(9)XCodeでビルド
(10)作成された products/*****.app を zipにする

申請用アプリのバイナリ送信

これをするには、iTunes Connectのステータスが Waiting For Upload になっている必要がある

(11)MacOSにインストールされている Applocation Loader を起動
(12)途中のダイアログで、申請中のアプリを選択
(13)バイナリは zipで固めたやつを指定

正常にバイナリが送信できたら、iTunes Connect のステータスが Upload Recieved もしくは Waiting For
Review になる

レビューを待つ

1週間以内でおわるっぽい。
私は年始だったけど、3,4日ぐらいだった。

Reject

Rejectされると、メールが届きます。
私も無事届きました(笑)
内容は、backlogへアクセスできる、スペース/ユーザ情報をくれ ということでReject.
これらの情報はReviewNoteに書けばOKらしい。

リリース

ステータスが Ready for Sale になれば、リリースです。
Storeにまだ並んでないときがあるかもしれないですが、少しまてば出てきます。

リリースの宣伝


のBadge画像は、↓ から取得できます。
http://developer.apple.com/appstore/resources/marketing/
Badge使うにあたっての制限があるので注意。

バージョンアップ


iTunesConnectのアプリのページに、add versionというボタンが増えるので、
ここから申請する。
レビュー後のリリースタイミングは日付指定もできる。

-- おわり --

補足。
アイコンは、plistに設定したもので、57*57, 72*72, 114*114 の3つと、iTunesConnectで登録する 512*512 の合計4つを使いました。
いろんなサイズ & 大きなサイズがいるので、ベクターで書くのがおすすめ。
• • •

2011年1月12日水曜日

BacklogのiPhoneアプリをリリース


BacklogのiPhoneアプリ: CrossBacklogをリリースしました!

以下の特徴があります。
- 複数のスペース、プロジェクトをまたいで表示
- 表示プロジェクトの選択機能
- ブックマーク機能
- アイコン表示機能

複数のスペース、プロジェクトを並行で使っていて、個々に見るのが面倒〜っという方にはぴったりなものになっています。
無料なんで、ぜひぜひダウンロードしてみてくださいね!



不具合とか要望とかあったら、twitter : @aqubi までご連絡くださーい!

ちなみに、要望があった絵文字なんですがー。
Backlog上で入力された絵文字は出来る限りiPhone上でマッピングして表示してます♪
iPhone上で入力された絵文字は、マッピングしているものはBacklog上でも表示されますが、マッピングしていないものは文字化けになります。(ずっとiPhone上でみるなら平気なんですけどもね..) これは今後の課題。
• • •

2011年1月3日月曜日

昨年の振り返りと今年の目標

あけましておめでとうございます。

去年を振り返ってみましたが、相変わらず 空うさぎ を触っている日々で自分自身にあまり進展がないー!
ということで、昨年の目標だった 「iPhoneアプリを作る」をなんとか実現するためラストスパート (+3日ほど年またぎ) していました。

モノは、すこしずつ進めていた CrossBacklog
先ほど Waiting For Review のステータスになりました。
果たしてRejectされるのか? 楽しみですw

さて、今年の目標ですが、

「収入を得るのを目標としたアプリ作成」

で行こうかと思います。

CrossBacklogでは無料+iAdにしていますが、そもそもBacklogユーザ限定ということもあり、そもそも収入を得たいためのアプリじゃない。
で、今年は収入を狙ったアプリケーションを、どんな言語/プラットフォーム/Storeでもいいので作成してチャレンジしようかと思っております。

今までは、単純に自分が欲しいアプリを作成していましたが
- どんな内容のものであれば買ってくれるのか?
- いくらぐらいだったら売れるのか?
....と、未知なる領域突入。

2個3個はアプリをリリースしてみたい & 空うさぎ もさらに進化したい ので お仕事以外の限られた時間 をどれだけ有効的に時間を使えるか?
も常に意識しないといけませんなぁ。

今年は「成長したなー」って思える年になるよう頑張ります♪
• • •