Hatena::ブログ(Diary)

Over&Out その後 このページをアンテナに追加 RSSフィード Twitter

2015-02-14

WatchKitを実際にさわってみてわかったこと@iOSオールスターズ勉強会 #dotsios

本日開催された「iOSオールスターズ勉強会」という勉強会に登壇させていただいた際の資料です。



※Keynoteでつくってpdfで書きだしてアップしたので、残念ながら埋め込んでいた動画が見れなくなってます


概要

スライドを見るのが面倒な方々のために、ざっくり概要を書いておきます。

1. アニメーション編

WatchKit App 側に入れてある「Staticな」リソースを使用するアニメーションは `setImageNamed:` と `startAnimatingWithImagesInRange〜` を使えば高速にアニメーションできるのですが、WatchKit App の Asset Catalog にない「Dynamicな」リソースを使用する場合はどうアニメーションを実装すべきか?という話。

  • フレームごとの UIImage オブジェクトを生成して、NSTimer で一定間隔ごとに `setImage:` する、というのはパフォーマンス的に全然アウト、それらのフレーム画像をキャッシュするのもプログラミングガイドに非推奨と明記されている
  • UIImage の `animatedImageWithImages:duration:` を利用してひとつの Animated な UIImage オブジェクトを生成してから `setImage:` するのが正解。

f:id:shu223:20150214162258g:image


2. テキスト入力編

こういうアニメーションする3D絵文字とか、


f:id:shu223:20150214162259g:image

(Apple Storeの動画より)


音声入力とか、Smart Replies(フレーズを選択して返信)


f:id:shu223:20150214162300p:image:w400

(同じく、Apple Storeの動画より)


WatchKit から利用可能なのか?できるとして、どうやって実装するのか?という話。


3. カスタムUI編

Appleによるヒューマンインターフェースガイドラインに載ってたこういうUI、


f:id:shu223:20150214162301p:image:w600


こういう Core Graphics でやりたくなる感じの描画できるの?どうやるの?という話。


他に紹介したのは、

  • カスタムフォント
    • 普通にできた

f:id:shu223:20150214162302p:image:w240


  • オーバーレイ

- WKInterfaceObject は、UIView の subview 的にインターフェース同士を重ねられない

- UIEdgeInsets 的なものもない

- → WKInterfaceGroup に設定できる背景画像を利用する

f:id:shu223:20150214162303p:image:w240


  • origin の調整

- WKInterfaceObject には、(UIView における) frame.origin 的なプロパティがない

- → WKInterfaceGroup を活用するとそれっぽく調整できる

f:id:shu223:20150214162304p:image:w240


WatchKitTrials

WatchKitにまつわる諸々の試行錯誤サンプルをまとめたリポジトリです。


https://github.com/shu223/WatchKitTrials

(近日push予定)


余談:ボツネタ集

個人的にはいままでで一番の大舞台だし、他の登壇者の方も濃くて役立ちそうな話をしてくれそうな方々ばかりなので、発表内容は結構悩みました。。


  • ボツネタ1. 「iOSのセンサ処理 再入門」

もう iOS も 8 になって、Swift という新言語まで登場しちゃって、どんどん進化していきますが、最近になって意外とわかってないなーと気付いたのが、もうかなり昔からある加速度センサとかジャイロスコープの値の処理の方法。


たとえば、iPhone をブンッと振ると、振っている間に加速度センサの値が大きく動くんじゃなくて、止めた瞬間にピークが立ち、直後に反対方向にまたピークが立ちます。これらをどう処理するのが定石なのか?とか。


あと、ユーザー加速度とか重力とかクォータニオンとかオイラー角とか、それらを扱うための Core Motion とか GLKit とか。


ボツ理由:結局のところ自分は興味があるだけで専門分野ではないので、大舞台で不正確なことをいってしまう恐れがありやめました。


  • ボツネタ2. 「今日から始めるBLE」

スライドの最後のページでも告知している通り、『iOS x BLE プログラミング 〜Core Bluetooth 徹底解説〜』(仮)という著書が来月発売予定なので、宣伝も兼ねられるのと、本書いたばかりなので自信をもって話せるということで、この方向性も検討しました。


ボツ理由:BLEとか外部デバイス連携の話って、iOSエンジニアの中でも興味がある人が限られるので、400人の前で話す話じゃないなー、ということでやめました。


  • ボツネタ3. 「iOSのバックグラウンドでできることできないことのまとめ」

Core Bluetooth の機能に、「状態の保存と復元」という、アプリが停止しても(プロセスが殺されても)iOS が BLE の処理のそのまま引き継いで、必要に応じてアプリを起こしてくれる という超強力なものがあり、各バックグラウンドモードの機能とか制約を知ることは非常に有意義だなーと。バックグラウンドモードも位置情報とかオーディオとかいろいろあって、リファレンスとかプログラミングガイドをそれぞれ熟読したわけじゃないし。


ボツ理由:さまざまなバックグラウンドモードについて詳しく調べるのが大変なのであきらめました。


※ちなみに Core Bluetooth の「状態の保存と復元」については拙著に詳しく書いたので興味のある方は是非!


おわりに

スライドに書いたこと以外にも、通知機能の検証がシミュレータでどこまでできるかとか、WatchKit Extension 側にも Parse SDK を入れて親アプリと同じユーザー(アカウント)になってデータのやりとりをするとか、いろいろ「実際にさわってみてわかったこと」があったのですが、発表時間は20分しかないので、今回は見た目的にわかりやすい話に絞りました。


他の発表者の方々の話も非常に勉強になりました。参加者のみなさま、主催の dots. と登壇者のみなさま、どうもありがとうございました!


2015-01-31

独立してから第4四半期目の実績まとめ

2014年11月〜2015年1月にフリーランスとしてやらせていただいたお仕事をまとめました。


3ヶ月毎にまとめつづけて今回で4回目、すなわち独立して一周年!!!!なわけですが、振り返り的なことは昨年の暮れに書いたばかりなので、今回はさらっと。


(過去のまとめ)


書籍執筆

この3ヶ月の仕事量を請求書ベースで数えてみると合計で37人日。平日だけで考えても丸1ヶ月ぐらいは休んでた計算になります。(3ヶ月でざっくり60営業日として)


が、もちろん休んでたわけじゃなくて、むしろ土日祝日・大晦日・元旦関係なく働いておりました。


何をそんなにがんばってたかというと、ずっと前からブログでもたまに触れてた「iOS✕BLEの書籍」を書いてたのでした。


「ページ数が足りない・・・」*1と夢に見たりもしましたが、どうにかこうにかひと通りは書き上げ、初校→修正→再校→修正→三校→修正・・・とあと何往復かすれば無事書店に並びます。


またタイトルや発売日等、詳細が決まり次第お知らせしたいと思います。


Macアプリ開発(Moff)

Moffのお仕事で、Mac OS Xアプリを開発しました。iOSでリリース済みのMoffアプリのMac版・・・ではなくて、Moffのアルゴリズム検討で用いる社内用ツール的なアプリです。


f:id:shu223:20150203075136p:image:w600


初めてのMacアプリでしたが、手馴れている ObjC + Xcode で書けて、それでいて何か新しい世界が拓けていく感じがするという、すごく楽しい体験でした。


【新規】とある新ウェアラブル案件

詳細は会社自体が何らかのプレスリリースとか出すまで控えたいと思いますが、BLEと音声処理が絡んでて、かなり楽しい案件。


【新規】とあるロボット案件

こちらも詳細はまだ控えます。BLE絡み。


【新規】とあるWatchKit案件

こちらも詳細は秘密ですが、WatchKitを使う案件。


色々と「実際にさわってみてわかった」知見がたまってるので、今度登壇させていただく「iOSオールスターズ勉強会」ではそのあたりの話をさせていただこうかと。


WHILL

アップデート版の開発。ちょっとWHILL本体の方が忙しそうで、月に1〜2人日のスローペースでした。


新しいリリースはないのですが、WHILLの注目度のおかげで、お手伝いしたアプリがTVに出ることもしばしば。


中でもこれはメチャメチャ嬉しかったです。



12月5日放送のダウンタウンの番組『教訓のススメ』でWHILLが紹介され、スタジオで蛭子さんが乗るWHILLを松ちゃんがアプリから遠隔操作する、という場面がありました。


僕の実装したアプリを松ちゃんが操作した、ということは、松ちゃんのタッチ操作を僕が書いたプログラムが処理し、実行したわけで、ある意味僕はついに松ちゃんと会話した!といっても過言ではないのではないでしょうか!!!!(キモくてすいません)中学生の頃ごっつを毎週録画しては2,3回観てたダウンタウンの洗礼をもろに浴びた世代として光栄至極です。

当時のFacebook投稿より)


あと最近知りましたが、WHILLのUSのサイトに、アプリの紹介ページができてました。


f:id:shu223:20150203075137j:image:w400


アプリはご好評をいただいているとのことです。


SmartDrive

こちらも引き続き。リリース間近なので集まってガッと集中してやろう、ってことで開発合宿に行ってきました。



開発は捗ったしご飯は美味しかったしで大変よかったです。


その他

めでたいことが多い3ヶ月でした。


TechCrunchハッカソンに「特別参加エンジニア」として参加&入賞!

あの増井さん(@masuidrive)と並んで、「特別参加エンジニア」としてTechCrunchハッカソンに呼んでいただきました。


iOSエンジニアのイベントでもないので、99%の参加者の皆様は「誰?」という感じだったと思いますがが、講演とかするわけじゃなく、普通に一緒に参加者としてチームに入ってハックする役回りなので、ひたすら手を動かして、徹夜もして楽しかったです。


f:id:shu223:20150203084837j:image:w500

(賞もいただきました!)



「Yahoo クリエイティブアワード」グランプリ!!!!

「Yahoo! JAPAN インターネット クリエイティブアワード 2014」で僕の名刺が超ありがたい賞をいただきました。


f:id:shu223:20141127080832j:image:w600



WBS(ワールドビジネスサテライト)出演!


海外iOS技術書籍にレビュアーとして参加

"Learning iOS 8 for Enterprise" という本。GitHubのiOS8-Samplerがきっかけでご依頼いただきました。



レビュアーということで、バイオグラフィーを載せていただきました。


おわりに:2年目の抱負

つい先週、「モイめし」というツイキャスを運営するモイ代表の赤松さんとの対談番組に出させていただいた際に、赤松さんに不意に尋ねられた際の回答。

いろいろと悩んでいるところではありますが、昨年も目の前の仕事を頑張っていったら何となく良い方向に流れていったので、今書いている本をきっちり書いて、抱えている仕事をきっちりして、それをきっちり発信するということを粛々とやっていきたいと思っています。


というわけでとくに具体的な戦略はないのですが、世の中の流れを見つつ自分の興味に正直に、粛々とやっていきたい所存です。引き続きよろしくお願いします!


*1:ひたすらMarkdownで書いてて、自分がどのくらい書いたのか最後にDTP作業が入るまで把握が困難。技術書はコスト的に一定ページ数以上ないと出せない

2014-12-29

フリーランスを1年やってみて考えが変わったこと

お客さん仕事はわりと早いうちに納まったのですが、原稿書きがまったく納まる気配がないので、(現実逃避として)今年1年をふりかえろうと思います。


といっても「こういうお仕事をやりました」的なのは3ヶ月ごとにまとめているので、

ここでは主に、独立して最初の1年目 *1 を振り返ってみて「フリーランスになってみると意外だったこと」「考えが変わったこと」「やりかたを変えたこと」あたりの話を書こうと思います。


出勤した方が楽しい

独立当初は、フリーランスという働き方のメリットのひとつが、「場所・時間の自由」だと思っていて、必要な打ち合わせのとき以外は、家とか、自分が入居しているコワーキングスペースで作業するようにしていました。


で、お客さんもほとんどの場合それでいいと言っていただけるのですが、今は逆に、僕の方から「オフィスで一緒に仕事させてほしい」とお願いしています。


その理由としては、

  • お客さんにすぐ確認できる・・・優先度や不明点を確認でき、作業の無駄が減る
  • 遠隔でのコミュニケーションが苦手・・・誤解を与えない言い回しに苦労したり、でもそのあたりをサボるとお互い不要にイラッとしたり
  • 温度感がわかる・・・直接話したり、一緒にランチしたりする中でその会社の状況とか雰囲気とか、やろうとしていることが実感値として理解できる
  • 集中できる・・・やはり1人でやるより緊張感がある
  • 見積もりレス・・・後述

などなどあります。


寂しくない

「会社には仲間がいるが、フリーランスは1人でやるので、寂しい」


独立前は僕もそう思っていたのですが、意外とやってみると逆の印象を受けました。

  • フリーランスでも結局開発体制としてはチームの一員であることに変わりはないので、「一緒につくっている仲間」という感は会社のときと同様にある
  • 個人的なコミュニケーションも仕事に繋がったりするので、勉強会や飲み会に行く時間が以前よりもっと有意義に感じるようになった

といった理由から、独立前よりもコミュニケーションや人付き合いは活発になった感があります。


個人でも楽しい案件に関われる

フリーランスになるにあたって迷いが少しあって、その理由のひとつが「会社の大看板に来る仕事の方が楽しいんじゃないか」というものでした。実際、カヤック時代にやった仕事はどれもおもしろくて、やりがいがあって、実績になるものだったので。


で、蓋を開けてみると、今年やらせていただいた仕事はどれもおもしろくて、やりがいがあって、実績にもなるものばかりでした。


今年は運がよすぎただけかもしれませんが、とにかく粛々と腕を磨いて、ちゃんとやりたいこととかやったことをアピールし続ければ個人でも道は開けるものだなと。


見積もりを(あまり)しなくなった

見積もりが結構苦手でして。。


たいていの場合、見積もり時点でUIデザインとか、動き(遷移とかタップしたときのギミック的なもの)とかって決まってることってほとんどなくて、でもiOSアプリの場合そこがそもそも開発の肝だったりするわけで。標準UIコンポーネントのプロパティをいじる範囲で済むなら3分だけど、自分でカスタムUIコンポーネントつくるなら丸1,2日かかるかもしれないとか。


で、ほとんどの場合、企画書にある情報だけだと


「1人日〜3人月ですね」*2


とか答えたくなるわけですが、お客さんに対してそういうわけにもいかないので、


「ここはどうしますか?たとえばこうしたい場合、選択肢としてはAとBがあって、それぞれメリット・デメリットは・・・」


とかやりたいことを引き出すことが必要になり、そういう不明確なところを質問にまとめたり、A案・B案みたいな枝分かれをさせつつ語弊や認識違いのないように・・・と回答を整理していくと、見積もりだけで8時間とか平気でかかってしまいます。


かといってざっくり見積もるとトラブルの元になるのでよくない、かといってお客さんからすると見積もりがないと始まらない、最初からUIデザインや詳細要件が明確になっていてほしいというのも無理がある。。



そんなわけで、最近は「お客さんのところに行って1日作業して、その日数 ✕ 人日ぶんいただく」という稼動日ベース方式でやっています。いつどれぐらいお客さんのところに行って作業するかは、その時々でお互いの状況や残りタスクの量を見て相談する感じで。


このやり方だとお客さん側にもいろいろとメリットがあると思っていて、

  • 状況にあわせて仕様とか優先順位を日々変更できる
  • 見積のバッファみたいなものが入ってこないので、無駄が少ない

みたいなことがあるかと。


ただこれって、「堤の1日の作業」に対するお客さんの信頼ありきなので、最初は1人日でどれぐらいできそうか、みたいな目安は提示したりはします。



ちなみにプロトタイプ系の案件は例外で、規模的に数人日レベルのものは十分見通しが立つので、お客さんに不明事項を確認して普通に見積もり出してやってます。*3


おわりに

先にもちょっと書いたように、もともとフリーランスになるにあたって迷いもあったわけですが *4、約1年やってみた今となっては、働き方としてかなり自分に向いてるんじゃないか、と感じています。

  • 自己責任で、自分の働き方・生き方を自由度高く選択できる
  • 自分の行動の結果が、自分にダイレクトに返ってくる

というあたりがやっててとても心地いいなと。


というわけで2015年も引き続きフリーランスとしてがんばっていく所存ですので、どうぞよろしくお願いいたします!ではみなさま良いお年を。


*1:ちなみに2014年2月に独立しました。

*2:標準コンポーネントを並べるだけで、「それっぽく」つくるだけなら結構1日でできたりしますよね

*3:あと、今はスタートアップのお仕事が多いのでたまたまこれで成立しているだけで、もうちょっと規模の大きい会社とお仕事させていただく場合は普通に見積もりレスなんて無理かもしれません。

*4:独立の経緯について話したインタビュー: http://prosheet.jp/blog/engineer/4641/

2014-12-19

iOSエンジニアが初めてAndroid開発をやってみた第1日目のメモ

当方フリーランスエンジニアですが、iOS専業でやっております。これまで幾度と無く「ちなみにAndroidの方は・・・?」「すいません、できないんです・・・」と肩身の狭い思いをしてきましたが、ついに今日第一歩目を踏み出しました。


ちなみに現在のスペックですが、

  • iOSアプリ開発歴4年、Android開発歴ゼロ
  • Androidはユーザーとしても経験値ほぼゼロ(先週 Nexus5 購入して WiFi セットアップしただけ)

という感じです。


実況中継的に随時追記する形で書いていきたいと思います。iOS からプログラミングの世界に入り、これから Android もやってみたい、という人も今となっては結構多いんじゃないかと思うので、どなたかの第一歩目の参考になれば幸いです。


※(終了後に追記)あとから読んだ人には何のことかわからないかもしれませんが、本記事は昼ぐらいに出だし(午前中に進めた内容。書籍購入まで)をアップし、その日何かやる度に実況中継的に追記していったものです。思いつくままに手を付けていった順に書いてるので、勉強しやすい順序とかにもなっていないと思います。


Android Studio インストール

何はともあれ開発環境のインストールから。(Android Studioは、iOSでいうXcode みたいなもの)

  • http://developer.android.com/sdk/index.html
  • Setup Wizard 通り
  • 途中で JDK 7.0 のダウンロード&インストールが必要(リンクはウィザード内で提示されるので事前準備は不要)
  • Standard と Custom 選択するところがあるがとりあえず Standard
  • License Agreement の後、そこそこ長いダウンロードとインストールがある

プロジェクト作成&エミュレータで動かしてみる

開発環境を入れたところで「で、どうしようかな。。」と一瞬途方に暮れたかけたのですが、iOSだとどうするかな、と想像してみて、何はともあれプロジェクト作成してエミュレータで空のプロジェクトを動作させてみるのがいいのかなと。

  • バージョンはとりあえず 4.4 (自分の Android がそうだったので)以上
  • テンプレートは違いがわからないので Blank
  • ActivityNameとかLayoutNameなどなど、それぞれがどこに影響するかわからないのでデフォルトのままで

で、上部のバーに Xcode の Build & Run ボタンと同じようなのがあったので、


f:id:shu223:20141219131748p:image


押してみると、デバイス選択っぽいのが出てきて、無事エミュレータで動作しました!


f:id:shu223:20141219131817p:image:w250


ただ、ロック画面みたいなところから起動したので、Androidの操作自体がわからなくてちょっと困惑しましたが。。


書籍を入手

最初はWebでググるにも用語自体がわからなかったりするので、近所の書店に行って、参考書籍を購入しました。


つくるべきものは決まっているので、サンプルをチュートリアル的に進めるタイプのものではなく、「テーブルビュー的なのはどう実装するんだろう」という感じで逆引き的に調べられそうかどうか、を判断基準に、書店で目次を確認して選びました。


書籍1:Android StudioではじめるAndroidプログラミング入門

Android StudioではじめるAndroidプログラミング入門
掌田 津耶乃
秀和システム
売り上げランキング: 37,320


入門とありますが目次見るとわりと網羅的な感じがしたので購入しました。今こうやって見るとAmazonではなかなか厳しいレビューついてますね。。


書籍2:Androidアプリ開発逆引きレシピ (PROGRAMMER’S RECiPE)

Androidアプリ開発逆引きレシピ (PROGRAMMER’S RECiPE)
株式会社Re:Kayo-System
翔泳社
売り上げランキング: 94,035


逆引きレシピが欲しかったので最初に手に取りました。出版年月も新しめだし。


書籍3:Androidアプリ開発パーフェクトマスター―Android4/3/2.2完全対応 (Perfect Master)

Androidアプリ開発パーフェクトマスター―Android4/3/2.2完全対応 (Perfect Master)
金城 俊哉
秀和システム
売り上げランキング: 41,222


分厚すぎて絶対外に持ち出せないし、実用的じゃないんじゃないか。。と思いましたが、情報量は多いし、悩んでる時間がもったいないので購入。


UIImageView的なものを置いてみる

画像リソースの実態は、(フォルダ名の一般名称がわからないので自分の現在のプロジェクトで言うと)

MyFirstApplication/app/src/main/res

にあります。


が、画像の追加はプロジェクトウィンドウ内にある「drawable-mdpi」フォルダにドラッグ&ドロップするとのこと。(書籍1からの情報)


UIImageView的なUIコンポーネントは、Widgetsのところに「ImageView」というそのものズバリなものがあったので、それを配置。


画像の入れ方がよくわからなかったのですが(書籍1ではプログラムから入れていた)、ImageView を IB 的なところでダブルクリックしたら src 画像を選ぶダイアログが出てきて、無事 drawable-mdpi に入れたものを選択することができました。


f:id:shu223:20141219142442p:image:w220


UIViewContentMode的なプロパティと、このレイアウト周りがわからないけど、そのあたりは後回しで。。


ボタンを置いてみる

ボタンを押したらコンソールにログをはく、的なことをやってみます。

  • Widgetsに「Button」というのがあるので、それを配置
  • 右にあるプロパティウィンドウから、「onClick」を探して、「button_onClick」と入力
  • MainActivity.javaを開き、ボタンクリックのイベントハンドラを実装
public void button_onClick(View view) {
    Log.d("MyApp", "view:" + view);
}

(記念すべき初めてのコード!)


ボタンまわりは書籍1を、Log の書き方はググって下記記事を参考にしました。


で、これだとエラーが出たので、import を追記

import android.view.View;
import android.util.Log;

(使用クラスごとに必要なのか・・・!?そんなわけないと思うけど、おいおい学んでいきます。。)


f:id:shu223:20141219142623p:image:w220


ボタンを押すと、コンソールに次のように出力されました。

12-19 14:21:48.467 1911-1911/com.shu223.myfirstapplication D/MyApp﹕ view:android.widget.Button{9cf6a48 VFED..C. ...P.... 372,921-707,1065 #7f080041 app:id/button}


画面遷移

次は UINavigationController の push / pop 的な画面遷移を・・・と思ったのですが、買った書籍(総ページ数約2000!)を調べてみても見当たらず・・・*1


ググるとすぐに出てきました。


インテントは他のアプリと連携するときに用いるもので、他のアプリ(例えばブラウザアプリ、メールアプリ等)を起動することができます。


同じアプリでも他の画面 (Activity) クラスを起動する場合には、インテントを使用します。

そうなんですね、カルチャーショック。。


それはさておき、

画面の遷移は, 元画面(メイン画面)のアクティビティから次画面(サブ画面)のアクティビティを起動することで実現できる.

とのことなので、

  • メニューから新規アクティビティを作成
    • [File] > [New] > [Activity]
    • SubActivityと命名
  • SubActivity に Button を置き、onClick プロパティに button_onClick と入力
  • SubActivity.java に以下のコード(と必要なimport)を追記
public void button_onClick(View view) {

    Log.d("MyApp", "onClick:" + view);

    Intent intent = new Intent(SubActivity.this, MainActivity.class);
    startActivity(intent);
}
  • MainActivity.java のボタンのイベントハンドラを以下のように修正
public void button_onClick(View view) {

    Log.d("MyApp", "onClick:" + view);

    Intent intent = new Intent(MainActivity.this, SubActivity.class);
    startActivity(intent);
}

上記記事では AndroidManifest.xml へ SubActivity の定義を追加する必要がある、とありますが、自動的に追加されてました。


f:id:shu223:20141219152052g:image


テーブルビュー的なものを実装する

iOS でいう UITableView は、Android では「リストビュー」と呼ぶそうです。で、「アダプター」なるものが UITableViewDataSource 的役割をするっぽい(いや、それ以前の、単なる配列かも。。)

  • Containers にある「ListView」を配置
  • SubActivity.java の onCreate メソッド内に、以下のコードを追記
ListView listView = (ListView)findViewById(R.id.listView);
ListAdapter adapter = new ArrayAdapter<String>(this, android.R.layout.simple_dropdown_item_1line, DAYS);
listView.setAdapter(adapter);
  • 同じく SubActivity.java に、以下のコードを追記(static な配列を生成)
private static final String[] DAYS = new String[] {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};

以上、「書籍2」の写経です。正直なところ、`R.id.listView` の `R` とか、`android.R.layout.simple_dropdown_item_1line` が何なのかとか全然わかっておりません。。


f:id:shu223:20141219162621p:image:w220


ちなみに コードから IBOutlet 的に UI オブジェクトにアクセスするにはどうしたらいいのか と思ってましたが、上記のように `findViewById` メソッドで取得するのが定石のようです。iOSでいえば `getViewWithTag` ですね。id は自動補完で出てきますが、IB的な画面の Properties の「id」からも確認できます。


実機で動かしてみる

ここまで忘れてたのですが、せっかく Android 端末を買ったので、実機で動かしてみます。


実機の「デベロッパーモード」を有効にする必要があるとのこと。「書籍3」に全編スクショ付きで載ってたので、Androidの使い方がわからない自分でも簡単でした。

  • [設定] > [端末情報]
  • 「ビルド番号」のところを7回タップ(おもろいw) -> デベロッパーモードになる
  • [設定] > [開発者向けオプション] > [USBデバッグ] を有効にする

あとはエミュレータの代わりに、実機を選択するだけ。


f:id:shu223:20141219165446j:image:w300


CSVファイルの読み込み

書籍に見当たらなかったのでググりました。


新規クラスファイルの生成方法がわからなくてけっこう困ったのですが、

app/src/main/java の位置で [File] > [New] しないと、メニューに「Java Class」というのが出ないようです。さらに、「com.shu223.myfirstapplication」と書かれているディレクトリ(パッケージ?)の下に置くと、他のクラスから扱えるようになるようです。


あと「プロジェクトの一番上」という表現もよくわからなかったのですが、 app/src/main 直下に assets という名前でディレクトリを作成すればOKでした。


  • assets ディレクトリを作成
    • 直下に csv ファイルを置く
  • CSVParser クラスを作成
package com.shu223.myfirstapplication;

import android.content.Context;
import android.content.res.AssetManager;
import android.util.Log;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

/**
 * Created by shuichi on 14/12/19.
 */
public class CSVParser {

    public static void parse(Context context) {
        // AssetManagerの呼び出し
        AssetManager assetManager = context.getResources().getAssets();
        try {
            // CSVファイルの読み込み
            InputStream is = assetManager.open("params.csv");
            InputStreamReader inputStreamReader = new InputStreamReader(is);
            BufferedReader bufferReader = new BufferedReader(inputStreamReader);
            String line = "";
            while ((line = bufferReader.readLine()) != null) {
                // 各行が","で区切られていて4つの項目があるとする
                StringTokenizer st = new StringTokenizer(line, ",");
                String first = st.nextToken();
                String second = st.nextToken();
                String third = st.nextToken();
                String fourth = st.nextToken();

                Log.d("MyApp", "Parsed:" + first + second + third + fourth);
            }
            bufferReader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

  • SubActivity から呼んでみる
CSVParser parser = new CSVParser();
Context context = getApplicationContext();
parser.parse(context);

Context についてはググって下記記事を参考にしました。


実行結果については省略。


ボタンに応じてクリックイベントの処理を分岐させる

MainActivity にボタンを2つ置いて、iOS でいう tag で判別して次に遷移する画面を切り替える、みたいなことをやるには、id プロパティを使用するようです。

  • MainActivity.xml にボタンをもう1つ追加
  • 2つのボタンの id をそれぞれ、「button1」「button2」とする
  • MainActivity.java のイベントハンドラの実装を次のように変更する
public void button_onClick(View view) {

    switch (view.getId()) {
        case R.id.button1:
        {
            Intent intent = new Intent(MainActivity.this, SubActivity.class);
            startActivity(intent);
            break;
        }
        case R.id.button2: {
            Intent intent = new Intent(MainActivity.this, BLEActivity.class);
            startActivity(intent);
            break;
        }
    }
}

参考:クリックイベントに応答する - Android 開発入門


プロジェクトの API LEVEL を変更する

「APIレベル」って初耳でそもそも何なのかわかってないのですが、BLE 関連のコードを書き始めると、巷に情報がよく出ているサンプルコードの多くは `startLeScan`、 `stopLeScan` という BluetoothAdapter クラスのメソッドを使っていて、どうやらこれらは API LEVEL 21 で deprecated になっているらしい。


で、仕方なく推奨されている新しい方を使おうと思ってみたものの、付け焼き刃すぎて、サンプルがないとよくわからない。。


APIレベルってそもそも何なの?と調べてみると、

21とか全然新しすぎる模様。iOS でいえば 8 の API を使ってたようなものか。


で、どこで API レベルが設定されていて、どう変更するのが適切なのかなと調べてみると、build.gradle というファイルを更新する、と書いてあったのですが、

Android Studio の [File] > [Project Structure] > [Flavors] からも変更できました。


ここで「Target Sdk Version」を 19 に変えて、「Sync Project with Gradle Files」ボタン押下、リビルドしました。


結果・・・deprecated なメソッドの警告はなくなりませんでした。。


で、もう一度 [Project Structure] から、[Properties] の 「Comple Sdk Version」 にマニュアルで 19 といれてみたところ、SDKのインストールが始まって、なんやかんやあって、無事「API LEVEL 21 では deprecated だけど、それより下のバージョンでは OKなメソッド」の警告が消えました。。


BLEを利用する

BLE(Bluetooth Low Energy)については参考書籍3冊とも一切記載なし。


が、ググるといろいろと参考記事が見つかりました。


実際にやってみると、deprecated 問題(前項参照)とかいろいろ大変だったのですが、下記手順でいけました。

<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
  • メンバ変数などの追加
private static final long SCAN_PERIOD = 10000;
private BluetoothAdapter mBluetoothAdapter;
private Handler mHandler;
static final int REQUEST_ENABLE_BT = 0;
  • 諸々初期化処理(onCreateにて)
BluetoothManager manager = (BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = manager.getAdapter();

if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
    Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}

mHandler = new Handler();
  • スキャン開始ボタンのイベントハンドラ
public void onClickScan(View v) {

    Log.d("MyApp", "onClickScan");

    // 10秒後にスキャン停止
    mHandler.postDelayed(new Runnable() {
        @Override
        public void run() {
            mBluetoothAdapter.stopLeScan(mLeScanCallback);
        }
    }, SCAN_PERIOD);

    // スキャン開始
    mBluetoothAdapter.startLeScan(mLeScanCallback);
}
  • ペリフェラルが見つかると呼ばれるコールバック
private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
    @Override
    public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
        // 見つかったペリフェラル情報をログ出力
    }
};

実行してみると、無事部屋にあるペリフェラルデバイスが見つかってました。

12-19 19:38:38.578    2287-2299/com.shu223.myfirstapplication D/BLEActivity&#65109; name=Kontakt, bondStatus=10, address=EE:FB:3B:6C:B8:BE, type2, uuids=
12-19 19:38:38.768    2287-2300/com.shu223.myfirstapplication D/BLEActivity&#65109; name=null, bondStatus=10, address=59:F9:0E:E7:AD:41, type0, uuids=
12-19 19:38:38.958    2287-2299/com.shu223.myfirstapplication D/BLEActivity&#65109; name=null, bondStatus=10, address=59:F9:0E:E7:AD:41, type0, uuids=
12-19 19:38:39.078    2287-2300/com.shu223.myfirstapplication D/BLEActivity&#65109; name=Kontakt, bondStatus=10, address=EE:FB:3B:6C:B8:BE, type2, uuids=
12-19 19:38:39.128    2287-2299/com.shu223.myfirstapplication D/BLEActivity&#65109; name=null, bondStatus=10, address=59:F9:0E:E7:AD:41, type0, uuids=
12-19 19:38:39.318    2287-2300/com.shu223.myfirstapplication D/BLEActivity&#65109; name=null, bondStatus=10, address=59:F9:0E:E7:AD:41, type0, uuids=
12-19 19:38:39.498    2287-2299/com.shu223.myfirstapplication D/BLEActivity&#65109; name=null, bondStatus=10, address=59:F9:0E:E7:AD:41, type0, uuids=

(iOS のセントラルマネージャのように、重複するペリフェラルについてはコールバックをまとめる、ということはやってくれない


アニメーション

PulsingHalo的なのを実装したくて着手していたのですが、所用につき今日はここまで。*2


所感

Android Studio という専用 IDE があるし、エミュレータもまぁ確かに遅いけど全然アリだし、関連書籍は充実してるし、ググれば大量に開発情報が出てくるしで、開発環境としては超快適だと思いました。iOSと比べてどうの、という不満は全くなし


ただ、やはり Android は、API群(SDK)がまるっきり違う上に開発言語や IDE も違うので、思った以上に「覚え直し」感がありました。。ひとつのアプリをプロダクト品質で出すまでにはまだまだ先が遠い印象。。


最近始めて Mac OS X アプリをつくったときは新しい世界が切り開かれる感じがしてワクワクしたのですが、やはり「モバイル端末でのアプリ開発」という似ている分野では自分はあまり新鮮味を感じられないのかなと。(ObjC->Swiftへのコンバートにもあまり興味が無いように)


そんなわけで、1日やってみた後の所感としては「おとなしく iOS の世界に戻ろう」という気持ちになっております。。


*1:あとでわかってみれば、載っていました。が、そもそもインテント、アクティビティという概念を知ってないと目次からは辿りつけない。。

*2:この後の予定としては、「レイアウト」「ボタン等の見た目のカスタマイズ」「デバッグ」などなど予定していました。

2014-12-12

iOSと機械学習

ビッグデータとかの機械学習隆盛の背景にある文脈や、その拠り所となるコンピュータの処理性能から考えても「モバイルデバイス向けOSと機械学習を紐付けて考えようとする」ことはそもそもあまり筋がよろしくない・・・とは思うのですが、やはり長くiOSだけにコミットしてきた身としては、新たに興味を持っている機械学習という分野と、勝手知ったるiOSという分野の交差点はないのかなと考えずにはいられないわけでして。。


そんなわけで、「iOS と機械学習」について雑多な切り口から調べてみました。


iOSで使える機械学習ライブラリ

DeepBeliefSDK

コンボリューショナルニューラルネットワークを用いた画像認識ライブラリ。iOSとかのモバイルデバイスで処理できるよう、高度に最適化してある、OpenCVと一緒に使うのも簡単、とのこと。


何はともあれ SimpleExample というサンプル実行したら、


f:id:shu223:20141211210821j:image


頼んでもないのにいきなりノートパソコンを認識しました!すごい!


SimpleExample のソースを見てみると、フレーム毎に得られるピクセルバッファの処理はこんな感じでした。

- (void)runCNNOnFrame: (CVPixelBufferRef) pixelBuffer
{
  assert(pixelBuffer != NULL);

	OSType sourcePixelFormat = CVPixelBufferGetPixelFormatType( pixelBuffer );
  int doReverseChannels;
	if ( kCVPixelFormatType_32ARGB == sourcePixelFormat ) {
    doReverseChannels = 1;
	} else if ( kCVPixelFormatType_32BGRA == sourcePixelFormat ) {
    doReverseChannels = 0;
	} else {
    assert(false); // Unknown source format
  }

	const int sourceRowBytes = (int)CVPixelBufferGetBytesPerRow( pixelBuffer );
	const int width = (int)CVPixelBufferGetWidth( pixelBuffer );
	const int fullHeight = (int)CVPixelBufferGetHeight( pixelBuffer );
	CVPixelBufferLockBaseAddress( pixelBuffer, 0 );
	unsigned char* sourceBaseAddr = CVPixelBufferGetBaseAddress( pixelBuffer );
  int height;
  unsigned char* sourceStartAddr;
  if (fullHeight <= width) {
    height = fullHeight;
    sourceStartAddr = sourceBaseAddr;
  } else {
    height = width;
    const int marginY = ((fullHeight - width) / 2);
    sourceStartAddr = (sourceBaseAddr + (marginY * sourceRowBytes));
  }
  void* cnnInput = jpcnn_create_image_buffer_from_uint8_data(sourceStartAddr, width, height, 4, sourceRowBytes, doReverseChannels, 1);
  float* predictions;
  int predictionsLength;
  char** predictionsLabels;
  int predictionsLabelsLength;

  struct timeval start;
  gettimeofday(&start, NULL);
  jpcnn_classify_image(network, cnnInput, JPCNN_RANDOM_SAMPLE, 0, &predictions, &predictionsLength, &predictionsLabels, &predictionsLabelsLength);
  struct timeval end;
  gettimeofday(&end, NULL);
  const long seconds  = end.tv_sec-- start.tv_sec;
  const long useconds = end.tv_usec - start.tv_usec;
  const float duration = ((seconds) * 1000 + useconds/1000.0) + 0.5;
  NSLog(@"Took %f ms", duration);

  jpcnn_destroy_image_buffer(cnnInput);

  NSMutableDictionary* newValues = [NSMutableDictionary dictionary];
  for (int index = 0; index < predictionsLength; index += 1) {
    const float predictionValue = predictions[index];
    if (predictionValue > 0.05f) {
      char* label = predictionsLabels[index % predictionsLabelsLength];
      NSString* labelObject = [NSString stringWithCString: label];
      NSNumber* valueObject = [NSNumber numberWithFloat: predictionValue];
      [newValues setObject: valueObject forKey: labelObject];
    }
  }
  dispatch_async(dispatch_get_main_queue(), ^(void) {
    [self setPredictionValues: newValues];
  });
}

・・・と決してシンプルとは言い難いですが、ここで行っている分類処理のコアは、

jpcnn_classify_image(network, cnnInput, JPCNN_RANDOM_SAMPLE, 0, &predictions, &predictionsLength, &predictionsLabels, &predictionsLabelsLength);

この1行にあるのかなと。で、あとはバッファまわりの諸々とか、得られた結果の処理とか。


おもしろそうなので、また別の機会にちゃんと見てみようと思います。


ML4iOS

iOSでの機械学習を行うためのオープンソースライブラリ。ライセンスは Apache License, Version 2.0 。


コミット履歴を見ると、3年前(2012年1月)からあり、つい最近も更新されています。


LearnKit

iOS, OS X 向け機械学習フレームワーク、とのこと。


サポートしているアルゴリズム

  • Anomaly Detection
  • Collaborative Filtering
  • Decision Trees
  • k-Means
  • k-Nearest Neighbors
  • Linear Regression
  • Logistic Regression
  • Naive Bayes
  • Neural Networks
  • Principal Component Analysis

READMEに下記のようなサンプルコード載ってるので、今度試す。

LNKNeuralNetClassifier *classifier = [[LNKNeuralNetClassifier alloc] initWithMatrix:matrix 
                                                                 implementationType:LNKImplementationTypeAccelerate
                                                              optimizationAlgorithm:algorithm
                                                                            classes:[LNKClasses withRange:NSMakeRange(1, 10)]];
[classifier train];

LNKClass *someDigit = [classifier predictValueForFeatureVector:someImage length:someImageLength];

使いやすそう。


mlpack-ios

mlpack を Objective-C プロジェクトにリンクできるようにしたもの、とのこと。


mlpack というのは「スケーラブルなC++機械学習ライブラリ」らしい。


Swift-Brain

Swiftで書かれた人工知能/機械学習ライブラリ。ベイズ理論、ニューラルネットワーク、その他AIが実装されているとのこと。


  • Matrices
    • Matrix operations
  • Machine Learning algorithms
    • Basic Regressions
    • Neural Networks
    • Support Vector Machines
    • Bayesian Classifiers
    • Self Organized Maps (maybe?)
    • Clustering
  • Statistics
    • Bayes Theorem/Naive Classifier
    • Kalman Filter
    • Markov Model

学習結果をiOSで利用

機械学習によって得られたモデル等をiOSで利用する」ケースです。


冒頭で、「iOSと機械学習を結びつけて考えるのはあまり筋がよろしくない」と書きましたが、このケースでは学習自体はiOSデバイスではなくバックエンド(という表現が正しいかは不明)で行うので、至極真っ当、というか王道かと。


画像認識

OpenCV for iOS に機械学習で作成したモデル(分類器)のxmlファイルをアプリに持たせれば、人の顔以外にもいろんなものを認識できますよ、という記事。



分類器自体はプラットフォーム依存ではないので、iOSという限定を外せばかなり色々とチュートリアル記事とかモデル配布記事がでてきます。


文字認識

iOS で使える OCR ライブラリ。


機械学習で精度を向上させたり、日本語を覚えさせたり。


参考:画像からのテキスト抽出:tesseract-ocr - Qiita


音声認識

こういう話でいえば、同様に、iOS用の音声認識エンジンも学習データを用意してモデルを自作することができます。

OpenEars は PocketSphinx (CMU Sphinx) というカーネギーメロン大学によるオープンソース認識エンジンのラッパーライブラリで、それ用の音響モデルを自作することはできます(とある案件で試したことあり)。


国産の「大語彙連続音声認識エンジン Julius」(iOSで利用可能)も、もちろん学習によりモデルを自作できます。


実例集

ストアに出ているアプリなど。


Deep Belief by Jetpac - teach your phone to recognize any object

上でも紹介した、DeepBeliefSDKを使ったアプリ。


f:id:shu223:20141211211050j:image:w200


このアプリでネコを認識するデモ動画。


Deep Learning

ディープラーニングで画像を分類するアプリ。


f:id:shu223:20141211211118j:image:w200


Summly

2011年12月と、ちょっと古い記事ですが、Summlyという「ウェブのコンテンツを箇条書きとキーワードの一覧に要約する」アプリのニュース記事。

まずは、特別なアルゴリズムでHTML処理を使って、ウェブページからテキストを抜き出すことから始まる。そのテキストを分析して、記事から選び出された「凝縮された部分」を箇条書きで吐き出す。Summlyのアルゴリズムは、いくつもの機械学習の手法と「遺伝的」アルゴリズム――進化をまねた発見的探索法――を利用してこれを行っている。

ダロイジオ氏のアルゴリズムは、さまざまな出版社によるいろいろなタイプの記事の、人間による要約を調査した。そしてこれらの要約を、Summlyが吐き出すべきものや、情報キュレーションを行う人間の仕事をうまく真似るための、メトリクス[尺度]を調整する際のモデルとして活用した。


その後 Yahoo! に買収され、Yahoo!のニュース要約とパーソナライズ表示、動画、画像検索機能等にその技術が用いられているとのこと。


Plant Recognition: Bringing Deep Learning to iOS

ディープラーニングで植物を認識する、という論文。


f:id:shu223:20141211211217j:image


おわりに

iOS と機械学習を絡めたお仕事、お待ちしております!


2009 | 08 |
2011 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2012 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2013 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2014 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2015 | 01 | 02 |