Hatena::ブログ(Diary)

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

2013-05-28

『iOSアプリ開発 達人のレシピ100』という本を書きました

2013年に入ってから4ヶ月間、無職のありあまる時間をつぎ込み執筆してきた本が、ついに本日発売となりました。



バルセロナで観光もせず執筆し、その後は鎌倉のコワーキングスペースに毛布を持ち込み半泊まり込みで執筆し、企画当初は230ページの予定だったところを大幅超過して370ページも書いてしまったほどに気合いを込めました。


タイトルには『達人のレシピ』とありますが、もちろん著者が達人というわけではなく、掲載しているレシピが達人レベルの難しさ、というわけでもなく、私が提案した『iOSアプリ開発に役立つTips』という地味なタイトルに対していろいろな大人の方々の意向が加わってこうなった、というものです(カッコイイので大変満足しております)。


本の内容

ざっくり言うと、iOSアプリ開発まわりのTips集です。


概要紹介代わりに、『はじめに』に書いた内容を転載します。

iOS SDK がカバーしている領域は非常に広大で、これだけ開発者が増え、書籍やオープンソースが充実した今でも、具体的な実装方法の情報が見つからない機能や、あまり知られていない機能も少なくありません。


本書では、書籍やインターネットでよく見かけるような定番レシピではなく、

  • 利便性が高いわりにあまり知られていない/情報が少ない機能を使うレシピ
  • 現場の開発者のかゆいところに手が届くレシピ
  • 応用が利き、開発の幅が大きく広がるレシピ

といったレシピを100 個、カテゴライズして紹介しています。


どれも実際に現場でiOSアプリ開発をおこなう過程で培われた「生きた」レシピですので、入門を終え、実際にアプリを開発されている方には役立つレシピががきっと見つかると思います。


ぜひ目次をご一読ください。


というわけで、ぜひ目次を見ていただき、興味のある項目がありそうであればご購入いただけると嬉しいです!




レシピ一覧(目次)

※本記事ではカラーの元画像を使用していますが、書籍自体は1色刷り(モノクロ)です。

Chapter1 アニメーション/演出
  • 001 CATransform3D を用いて3D 表現をおこなう


  • 002 3Dアニメーション
  • 003 UIKit で物理演算エンジンを使用する


  • 004 UIKit 上でパーティクルエフェクトを表示する


  • 005 Core Image の遷移エフェクトを使う


  • 006 パスに沿ってアニメーションさせる
  • 007 グラデーションカラーをアニメーションさせる


  • 008 画面を揺らすエフェクト
  • 009 クロスディゾルブで画面遷移
  • 010 UIView のBlocksアニメーションをキャンセルする
  • 011 トゥイーンライブラリを使用する:PRTween
  • 012 トゥイーンライブラリを使用する:AHEasing


Chapter2 画像処理/色処理
  • 013 OpenCV for iOS を使う
  • 014 vImageで画像処理をおこなう
  • 015 シャッター音の鳴らないカメラアプリを実装する
  • 016 カメラにフォーカスと露出をタップで合わせる機能をつける
  • 017 Core Image の各フィルタのパラメータの詳細を調べる
  • 018 Core Imageでノイズ/チェッカーボード/ストライプ等のパターンを生成する


  • 019 トイカメラのようなトンネルエフェクト(ビネット効果)を写真に加える


  • 020 画像をポスタライズ(色数を減らす)する


  • 021 画像にモザイクをかける


  • 022 UIBezierPathでパスを描画する
  • 023 SVGファイルをUIBezierPath オブジェクトに変換する


  • 024 画像を多角形で切り抜く


  • 025 プログラムからスクリーンショットを撮影する
  • 026 画像の4 角を維持したまま拡大縮小する(9スライス)


  • 027 Core Graphics を用いてグラデーションを描画する


  • 028 UIColor の重ね合わせ/CSSカラー名での色指定をおこなう
  • 029 配色技法に基づいた色のセットを自動生成する


Chapter3 オーディオ処理
  • 030 OpenALで3D 空間に音源を配置する
  • 031 デバイスのボリュームボタンの操作を検出する
  • 032 ヘッドフォンジャックの抜き差しを検知する
  • 033 バイブを鳴らす
  • 034 システムボリュームに左右されない音量でシャッター音を鳴らす
  • 035 AVAudioRecordを用いてマイクに入力された音声を録音する
  • 036 AVCapture を用いてマイクに入力された音声を録音する

Chapter4 UI
  • 037 3D 表現を用いたUI


  • 038 UINavigationController の遷移アニメーションをカスタマイズする
  • 039 バウンスさせてビューを出す
  • 040 PathやFacebookライクなスライドメニューを実装する


  • 041 数値をバッジで表示する


  • 042 スクロール付きタブバーを実装する
  • 043 UIAlertViewを左寄せにする


  • 044 UIView サブクラスをxibファイルから生成する
  • 045 xibファイルのローカライズをInterface Builder上でできるようにする


  • 046 背景色にビルトインテクスチャを適用する


  • 047 GLKitを用いて描画処理を高速化する
  • 048 画面の解像度の種別を判定する
  • 049 Core Plot で折れ線グラフを描画する


Chapter5 テキスト


  • 051 正規表現を使う
  • 052 UILabelでのテキスト表示をリッチにする
  • 053 UILabel / UITextView / UITextFieldのテキストをNSAttributedStringで装飾する


  • 054 UILabel のテキストに効果をかける


  • 055 UILabel の文字列を立体的に見せる


  • 056 UIWebView からアプリ内に入れたカスタムフォントを使う
  • 057 UITextView やUIButton のテキスト位置を調整する
  • 058 UILabel / UITextView の高さを調整する
  • 059 英単語の単数形/複数形変換をおこなう
  • 060 UITextField への入力時にキーボードではなくピッカーを出す


Chapter6 位置情報
  • 061 Google Maps SDK for iOS を使用する


  • 062 MKAnnotationView のコールアウト(吹き出し)をカスタマイズする


  • 063 MKMapView のピンをタップせずにコールアウトを出す
  • 064 指定方向に指定距離離れた場所の緯度経度を計算する


Chapter7 WEB サービス連携
  • 065 UIActivityViewController の連携アプリを追加する


  • 066 カスタムUIAcitivityを実装する


  • 067 UIWebViewで表示中のHTMLコンテンツを取得する
  • 068 App Store のAPI からアプリの最新バージョンを取得する
  • 069 ユーザーにApp Store のレビュー投稿を促す


Chapter8 デバッグ
  • 070 Instrumentsを使わずに空きメモリ/使用メモリ/ CPU 負荷を取得する
  • 071 使用メモリ量やCPU 負荷をリアルタイムにアプリUI 上に表示する


  • 072 例外発生時にスタックトレースを出力する
  • 073 クラス名/メソッド名/コード内の何行目かをログ出力する


  • 074 静的アナライザによる解析を特定のファイルで無効にする
  • 075 アプリ内でログを閲覧する


Chapter9 開発ツール
  • 076 クラスの依存関係を図示する


  • 077 ドキュメントを自動生成する


  • 078 CSVファイルからスクリプトでplistを生成する
  • 079 otoolでアプリが使っているフレームワークの一覧を取得する
  • 080 Apple の開発用ツールをXcodeメニューに追加する

Chapter10 Xcode
  • 081 Xcode のファイルテンプレートを自作する
  • 082 Xcode のFunction Menu にコメントを表示する
  • 083 Xcode のRun Scriptを特定のConfigurationに対してだけ実行する
  • 084 Xcodeマクロ($(SRCROOT)や$(BUILD_DIR)等)の置換内容の一覧を調べる
  • 085 Xcode 4 のCode Snippet Libraryにコードスニペットを追加する


  • 086 ARC の有効/無効をファイルごとに設定する
  • 087 1つのプロジェクトで複数のBundle identifierを使い分ける
  • 088 カラーテーマファイルを用いてXcodeのエディタのカラーをカスタマイズする

Chapter11 その他
  • 089 デバイスにインストールされているアプリ一覧を取得する(方法その1)
  • 090 デバイスにインストールされているアプリ一覧を取得する(方法その2)
  • 091 .ipa ファイル生成を自動化する
  • 092 1行でiOSバージョンを判定する
  • 093 zipファイルを解凍する
  • 094 CSVファイルを読み込む
  • 095 通知名の有無をチェックする
  • 096 NSUserDefaults の内容を一括消去する
  • 097 構造体をNSDictionary やNSArray に格納する
  • 098 オブジェクトが持つプロパティの型と名前の一覧を取得する
  • 099 浮動小数点数の乱数を生成する
  • 100 GCD を用いてバックグラウンドで処理を遅延実行する


補足

  • ほぼすべてのレシピにサンプルコードがついています。
  • iOS6対応(表紙とか帯に入れてもらえばよかった。。)
  • 電子書籍版は、企画発生当初よりお願いしているのですが、いろいろとハードルがあるらしく、まだ今のところ予定がありません。。

買えるところ

(5/28追記)Amazonで売り切れになっていることがあるようなので、オンラインで買える(かつ在庫状況がその場で確認できる)ところをいくつかピックアップして載せておきます。


また、下記リンクから、本書の近隣のリアル店舗における在庫の有無が調べられます。

https://takestock.jp/book/4798038180/search/


2013-05-17

"dyld: lazy symbol binding failed: Symbol not found: _objc_setProperty_nonatomic_copy" エラーの対処方法

他人が過去につくったプロジェクトをビルドしてシミュレータで動かしてみると、

dyld: lazy symbol binding failed: Symbol not found: _objc_setProperty_nonatomic_copy

なるエラーが出てクラッシュました。


クラッシュしたシミュレータの iOS バージョンは 5.1 で、試しに 6.1 で動かしてみると問題なく動作します。


ビルド設定の "iOS Deployment Target" が 6.x とかになってるのかな、と見てみると、ちゃんと 5.1 になってます。


が、CocoaPods 経由でインストールされたライブラリ群の TARGETS のビルド設定の "iOS Deployment Target" が 6.0 になってました。


それらを全部(5.1 に)修正することで正常動作するようになりました。


2013-05-16

SLComposeViewController の isAvailableForServiceType: がシミュレータでは必ず YES を返してくる件

下記のコード、実機ではFacebookアカウントを登録していないと NO を返してきますが、シミュレータでは必ず YES が返ってきます。


[SLComposeViewController isAvailableForServiceType:SLServiceTypeFacebook]

地味な話ですが、このことを知らずに、「実機の Facebook や Twitter のアカウントを一旦消すのがちょっと嫌でシミュレータだけでチェック」とかうっかりやってしまうと、あとから実機で問題発覚、となりかねないので要注意です。


参考ページ



2013-05-15

Social.framework を利用して Twitter の DM を送信する

Social.framework (と Accounts.framework)を利用して Twitter の DM を送信する手順です。


1. フレームワークをプロジェクトに追加

以下の2つを追加し、

  • Social.framework
  • Accounts.framework

DM送信を実装するクラスでヘッダをインポートしておきます。

#import <Social/Social.h>
#import <Accounts/Accounts.h>

2. Twitterアカウントのアクセス権を取得

Twitterアカウントのアクセス権を取得し、ACAccountオブジェクトを取得します。

ACAccountStore *store = [[ACAccountStore alloc] init];
ACAccountType *type = [store accountTypeWithAccountTypeIdentifier:
                              ACAccountTypeIdentifierTwitter];

[store requestAccessToAccountsWithType:type
                               options:nil
                            completion:^(BOOL granted, NSError *error) {
                                
                                if (!granted) {
                                    
                                    NSLog(@"not granted");
                                    
                                    return;
                                }
                                
                                NSArray *twitterAccounts = [store accountsWithAccountType:type];
                                
                                if (!(twitterAccounts > 0)) {
                                    
                                    NSLog(@"no twitter accounts");
                                    
                                    return;
                                }
                                
                                ACAccount *account = [twitterAccounts lastObject];
                            }];

3. DM 送信

SLRequestクラスを利用して、direct_messages/new APIをたたきます。

NSURL *url = [NSURL URLWithString:@"http://api.twitter.com/1.1/direct_messages/new.json"];
NSDictionary *parameters = @{@"text": @"テスト送信",
                             @"screen_name": @"shu223"};

SLRequest *request = [SLRequest requestForServiceType:SLServiceTypeTwitter
                                        requestMethod:SLRequestMethodPOST
                                                  URL:url
                                           parameters:parameters];

request.account = account;
[request performRequestWithHandler:^(NSData *responseData,
                                     NSHTTPURLResponse *urlResponse,
                                     NSError *error)
 {
     NSError *jsonError = nil;
     id jsonData = [NSJSONSerialization JSONObjectWithData:responseData
                                               options:0
                                                 error:&jsonError];
     if (error){
         
         NSLog(@"error:%@", error);
     }
     
     if (jsonError) {
         
         NSLog(@"jsonerror:%@", jsonError);
     }
     
     dispatch_async(dispatch_get_main_queue(), ^{
         
         NSLog(@"jsondata:%@" , jsonData);
     });
 }];

上のコード内で使用しているパラメータについて補足すると、

  • text
    • 送信するテキスト
    • required
  • screen_name
    • 宛先ユーザーのscreen name(@のあとに続く名前)
    • option

となっています。


補足

iOS 6 より deprecated になりましたが、Twitter.framework の TWRequest を用いても同様のことができます。

TWRequest *request = [[TWRequest alloc] initWithURL:url
                                         parameters:parameters
                                      requestMethod:TWRequestMethodPOST];

参考資料

POST direct_messages/new | Twitter Developers


2013-05-13

突発的に1週間の短期語学留学に行ってきたメモ・完全版

突発的に1週間の短期語学留学に行ってきました。


思えば15年前にはじめて海外旅行をして以来、海外に行くたびに「英語を話せるようになりたい!」と一念発起しては何かに手をつけ、そして飽きる、というありがちなパターンを繰り返してきて、もう最後の手段は「英語を話す以外の選択肢がない」という状況に身を置ける語学留学しかない!ということで、実際に行ってみて自分的にはかなり良かったので、以下に諸々をまとめたいと思います。


行ったところ

行った学校は、フィリピンのセブ島にある、IDEA CEBU というところです。


http://philippine-r.com/cms/ideacebu01/


セブのこのへんにあります。



どこにも遊びにいってないので、このあたりが中心部なのかどうなのか、全然わかりません。近くにでかいショッピングモールはあります。奇麗なビーチとかは(たぶん)近くはないです。


全寮制で、部屋はこんな感じです。


f:id:shu223:20130509222007j:image:w300


シャワーとトイレがついてて、冷蔵庫があって、エアコンがあって、机まであります。シャワーはバリバリ水圧あって、アツアツのお湯が出ます。僕はシングルで入りましたが、ドミトリーもあります。


IDEA CEBUに決めたいきさつ

本の執筆の仕事がずっと押していて、次のアメリカでの働き先からも早く来てほしいといわれていて、語学留学に行く時間はなさそうなムードが濃厚だったのですが、ある日、翌日から5日ほどスケジュールに空きが出ることが急遽わかり、すぐさま


「明日から語学留学に行きたいんですけど!」


と『語学留学』でググって出てくるところに何件か電話してみたものの、


「遅くとも1ヶ月前には言ってもらわないと・・・」


とか、2週間以上前じゃないと受け付けられないとか、もう全然だめな感じでした。


そういうもんなのか。。といったん諦めつつ、ダメ元でセブ島になぜか住んでいる元同僚に「明日から語学留学に行きたいんだけどなんとかならないか」的なメッセージを送ってみたところ、頼もしいことに、直接経営者の方に聞いてくれるとのお返事。


そしてなんと翌日からでもいいよという話になったのですが、結局、やりとりしてるうちにもうその翌日になっていたので、いったん断念。


で、その1週間後ぐらいに執筆作業が少し前倒しで終わって時間ができたので、直接その経営者の方に連絡して申し込み、となりました。


というわけで、他の国や他の学校と比べたりとか、授業内容を吟味したりとかは全然してなくて、とにかく「緊急入学させてくれた」というのが決め手でした。


旅程

本の校正締め切りが5/2のAM10:00で、その日の夕方には日本を発つ、という強行スケジュールでした。

  • 5/2(木) 夕方に日本発
  • 5/3(金) 未明にセブに到着、寮で寝て、オリエンテーション、レベルチェック受けて、さっそく授業2コマ
  • 5/4(土) 追加授業4コマ
  • 5/5(日) 追加授業4コマ
  • 5/6(月) 授業6コマ+追加授業1コマ
  • 5/7(火) 授業6コマ+追加授業1コマ
  • 5/8(水) 授業6コマ+追加授業1コマ
  • 5/9(木) 授業2コマ+追加授業1コマ+卒業スピーチ
  • 5/10(金) 未明にセブ発、正午ぐらいに日本に到着

1コマ=50分です。


もともと提示されてたスケジュールでは土日は授業休み、平日6コマ、というものだったのですが、どこか遊びに行くよりも英語をやりたかったので、追加料金を払って授業を増やしてもらいました。


時間割

こちらの「1日の授業構成」ってところをご参照ください。


http://philippine-r.com/cms/ideacebu01/


朝7時からボキャブラリーテストというのがあって、健康的でいい感じです。


学校の規則

現地で説明を受けてから知ったのですが、いろいろと細かく規則があって、それが自分としてはよかったです。たとえばアルコールは学院内には一切持ち込み禁止、空き瓶が見つかるだけでもだめ、とか、平日は門限が20時とか。


僕の場合は現地で遊びたいっていう気持ちは全然なくて、とにかく英語力をアップさせたかったので、勉強以外の選択肢(誘惑)を規則で制限してくれてありがたい、と思いました。


あと、規則じゃないですが、インターネットがロビー、休憩所といった共用スペースにあって、部屋で繋げられないのも、地味に良かったです(誘惑が少ないという意味で)。


お値段

  • 飛行機代:84,760円(燃油サーチャージとか諸々込み)
  • コース代:53,500円(授業料、滞在費、食費、洗濯、掃除とか諸々込み)
  • 追加授業料:約15000円(追加1コマあたり約1250円)

使ったお金はこんなところです。3食出るし、遊びに行く時間もなかったので、現地では全然お金を使わず。


ビフォーアフター

気になる効果ですが、大いにあったと思います。


初日は聞き取れないししゃべれないしで辛かったのですが、帰るころにはだいぶ慣れて、普通に先生と会話してました。


自分の会話をよくよく振り返ってみると聞き取れてなかったり言いたいことを言えてなかったりするのですが、あんまりそういうことを気にしなくなった、という感じです。


あとは場数を踏めば表現の引き出しを増やしていけるので、「場数を踏む際の壁となっていた恐怖心がとれた」というのは収穫としてでかいと思っています。


f:id:shu223:20130512170103j:image:w300

最終日は全生徒/先生の前で英語でスピーチ


期間について

1週間は語学留学としてはすごく短いと思うのですが、僕にはちょうどいい長さでした。


場数を踏む、という点では1ヶ月でも短いし、プログラマとしては1ヶ月とかそれ以上現場から離れると、結構遅れをとるなーという感覚があります(忘れる、腕がなまる、とかじゃなくてその間に世の中が進んでしまう)。


とりあえず恐怖心がとれたので、あとはもう英語を使う場に飛び込んで本職に没頭しつつ周りとコミュニケーションとって場数を踏めばいいかなと。


まとめ

そんなわけで、個人的には1週間の短期語学留学はオススメです。



2013-05-10

1週間の短期語学留学に行ってきました

(2013年5月12日追記:こちらの記事は未完成です。完全版はこちらにあります)


1週間の短期語学留学に行ってきました。


思えば15年前にはじめて海外旅行をして以来、海外に行くたびに「英語を話せるようになりたい!」と一念発起しては何かに手をつけ、そして飽きる、というありがちなパターンを繰り返してきて、もはやどんな勉強も挫折が先に見えてしまって手をつけることすらできないという状態でした。


で、もう最後の手段は「英語を話す以外の選択肢がない」という状況に身を置ける語学留学しかないなーと。


そんなこんなで実際に行ってみて自分的にはかなり良かったので、以下に諸々をまとめたいと思います。


※いままさに帰宅の途中で、成田エクスプレス内で書いてるので、未完の部分が多々あります。


行ったところ

行った学校は、フィリピンのセブ島にある、IDEA CEBU というところです。


http://philippine-r.com/cms/ideacebu01/


セブのこのへんにあります。



全寮制で、部屋にはシャワーとトイレがついてて、冷蔵庫があって、エアコンがあって、机まであります。シャワーはバリバリ水圧あって、アツアツのお湯が出ます。僕はシングルで入りましたが、ドミトリーもあります。


IDEA CEBUに決めたいきさつ

本の執筆の仕事がずっと押していて、次のアメリカでの働き先からも早く来てほしいといわれていて、語学留学に行く時間はなさそうなムードが濃厚だったのですが、ある日、翌日から5日ほどスケジュールに空きが出ることが急遽わかり、すぐさま


「明日から語学留学に行きたいんですけど!」


と『語学留学』でググって出てくるところに何件か電話してみたものの、


「遅くとも1ヶ月前には言ってもらわないと・・・」


とか、2週間以上前じゃないと受け付けられないとか、もう全然だめな感じでした。


そういうもんなのか。。といったん諦めつつ、ダメ元でセブ島になぜか住んでいる元同僚に「明日から語学留学に行きたいんだけど」メッセージを送ってみたところ、頼もしいことに、直接経営者の方に聞いてくれるとのお返事。


そしてなんと翌日からでもいいよという話になったのですが、結局、やりとりしてるうちにもうその翌日になっていたので、いったん断念。


で、その1週間後ぐらいに執筆作業が少し前倒しで終わって時間ができたので、直接その経営者の方に連絡して申し込み、となりました。


というわけで、他の国や他の学校と比べたりとか、授業内容を吟味したりとかは全然してなくて、とにかく「緊急入学させてくれた」というのが決め手でした。


旅程

本の校正締め切りが5/2のAM10:00で、その日の夕方には日本を発つ、という強行スケジュールでした。

  • 5/2(木) 夕方に日本発
  • 5/3(金) 未明にセブに到着、寮で寝て、オリエンテーション、レベルチェック受けて、さっそく授業2コマ
  • 5/4(土) 追加授業4コマ
  • 5/5(日) 追加授業4コマ
  • 5/6〜8 授業6コマ+追加授業1コマ
  • 5/9(木) 授業2コマ+追加授業1コマ+卒業スピーチ
  • 5/10(金) 未明にセブ発、正午ぐらいに日本に到着

(追加授業については後ほど追記します)


時間割

こちらの「1日の授業構成」ってところをご参照ください。


http://philippine-r.com/cms/ideacebu01/


朝7時からボキャブラリーテストというのがあって、健康的でいい感じです。


学校の規則

現地で説明を受けてから知ったのですが、いろいろと細かく規則があって、それが自分としてはよかったです。たとえばアルコールは学院内には一切持ち込み禁止、空き瓶が見つかるだけでもだめ、とか、平日は門限が20時とか。


僕の場合は現地で遊びたいっていう気持ちは全然なくて、とにかく英語力をアップさせたかったので、勉強以外の選択肢(誘惑)を規則で制限してくれてありがたい、と思いました。


あと、規則じゃないですが、インターネットがロビー、休憩所といった共用スペースにあって、部屋で繋げられないのも地味に良かったです。


お値段

(のちほどまとめます)


ビフォーアフター

気になる効果ですが、大いにあったと思います。


初日は聞き取れないししゃべれないしで辛かったのですが、帰るころにはだいぶ慣れて、普通に先生と会話してました。


自分の会話をよくよく振り返ってみると聞き取れてなかったり言いたいことを言えてなかったりするのですが、あんまりそういうことを気にしなくなった、という感じです。


会話をすればするほど表現の引き出しが増えていく実感はあるので、あとは場数を踏むだけ。「場数を踏む際の壁となっていた恐怖心がとれた」というのが最大の収穫です。


期間について

1週間は語学留学としてはすごく短いと思うのですが、僕にはちょうどいい長さでした。


場数を踏む、という点では1ヶ月でも短いし、プログラマとしては1ヶ月とかそれ以上現場から離れると、結構遅れをとるなーという感覚があります(忘れる、腕がなまる、とかじゃなくてその間に世の中が進んでしまう)。


とりあえず恐怖心がとれたので、あとはもう英語を使う場に飛び込んで本職に没頭しつつ周りとコミュニケーションとって場数を踏めばいいかなと。


まとめ

(のちほどまとめます)


2013-05-02

評判のいいiOSアプリ開発のTips ベスト20

iOSアプリ開発に役立つTips』という Facebook ページをやっておりまして、そこで評判が良かった投稿(※)を 20 個ほど紹介します。


(※) Facebook ページの insights にある「クチコミ度」で判断しました


アプリ実行中にコードを修正してそのまま実行中のアプリに反映させる

アプリ実行中にコードを修正してそのまま実行中のアプリに反映させることができるXcodeプラグイン。


実機でもシミュレータでも使用可能とのこと。実機での動作検証中に修正して再インストールは時間かかるので、超ありがたいかも。


Injection for Xcode


「PCのwebブラウザからiPhoneのカメラロールにアクセスできるようにするアプリ」のしくみ

アプリ側でURLを発行している画面キャプチャがあるので、アプリにHTTPサーバ機能をもたせて(古いけどCocoaHttpServerとか)、アプリ側でカメラロールにアクセスしてその情報をweb経由で見せる、というしくみでしょうか。



新作!iPhoneのカメラロールをブラウザからアクセス出来るようにするアプリ「AirLib」を公開します | 関西/大阪のiPhone・iPadアプリ開発 feedtailor Inc. 社長ブログ


イラレで書いたパスデータを、UIBezierPathのコードに変換してくれるイラレ拡張

イラレで書いたパスデータを、UIBezierPathのコードに変換してくれるイラレの拡張。


UIBezierPath でコードだけでいい感じの図形とか画像を描くのは無理があるので、これは便利かも。


Drawscript - convert Illustrator shapes into code


「アプリを操作中の写真」を自動生成してくれるサービス

スクリーンショットの画像をアップすると、ハメコミ合成で「アプリを操作中の写真」を自動生成してくれるサイト。


ちょっと手元のスクリーンショットでやってみましたがなかなかいい感じの仕上がりでした。


PlaceIt by Breezi - Generate Product Screenshots in Realistic Environments


コマンドラインからJSONをごにょごにょする

特にiOSアプリ開発に限った話ではないのですが、jqという、「コマンドラインからJSONをごにょごにょできる」コマンドがかなり便利そうです。


たとえば、ルート直下の要素だけを取り出したい場合は、

$ jq ".[0]" hoge.json

"entities"要素内の"hashtags"要素内の"text"という要素を取り出したい場合は

$ jq ".[].entities.hashtags[].text" hoge.json

みたいな感じで簡単にとれて、はじめて使うAPIのレスポンスの構造を調べるときとかに活躍しそうです。


jqコマンドが実は高性能すぎてビビッた話


NSNumberFormatterで英語の勉強

NSNumberFormatterのいろいろなサンプル。3桁ごとにカンマ区切りにする「以外」のサンプルってあまり見つからないので参考になりました。


とくに、NSNumberFormatterSpellOutStyle がおもしろくて、たとえば、

[formatter setNumberStyle:NSNumberFormatterSpellOutStyle];
NSString *formattedOutput = [formatter stringFromNumber:@129];

こうやると、

one hundred twenty-nine

って出力してくれます。


これで英語の勉強アプリつくれそうです。


Formatting Numbers – NSNumberFormatter Examples


iPhoneの設定から通信速度を制限する

久しぶりに設定の「デベロッパ」のとこ見てみたら、この通信速度以外にも、iAdのテスト用設定とかも追加されてました。



iPhone5実機(iOS6.0.2)で通信速度を制限する #iOS #iPhone - Qiita


『Core Animation Programming Guide』大幅改定

『Core Animation Programming Guide』が大幅改定されたもよう。前回の更新から実に2年4ヶ月ぶり。『Animation Types and Timing Programming Guide』の内容もこっちに組み込んだとのこと。


Core Animation Programming Guide


CAEmitterLayer, CAEmitterCellを使ったパーティクルエフェクトの実装方法

iOS5からCore Animationに追加された、CAEmitterLayer, CAEmitterCellを使ったパーティクルエフェクトの実装方法について書きました。


UIKit上でパーティクルエフェクトを表示する - Over&Out その後


コマンドラインから.ipaをつくる

コマンドラインから.ipaをつくると"make adhoc"だけでできて楽ですよ、という話。


最初だけMakefileをつくるひと手間が必要ですが、curlコマンドも書いておけばビルド完了後に自動でtestflightにアップもできる、というのは便利そうと思いました。


.ipaをサッとつくる - blog.ishkawa.org


(派生記事)

.ipa ファイル生成を自動化する


バッテリーの超詳細情報を取得する

これを実機で実行すると、バッテリーの相当細かい情報まで取得できます。電圧までとれる。


ここまでとれるのか〜、とソースの中身みてみると、もろにプライベートAPI使ってました。。OSDBatteryっていうクラスがあるようです。


BatteryInfo


自然言語のテキストを属性で区分

NSLinguisticTaggerについて書きました。


自然言語のテキストを品詞(名詞、動詞、代名詞)や「個人名」「地名」といった属性で区分することができます。あと、日本語の形態素解析も可能です。



自然言語のテキストを属性で区分する - Over&Out その後


Auto Layoutの詳説スライド

70ページにわたるAuto Layoutの解説スライド(日本語)


Auto Layoutの基礎知識から、IBでの設定方法、ソースでの設定方法などなど。



iOS6 Auto Layout


画像を使用せずアイコンを描画

FacebookとかTwitterアイコンのUIButtonを描画してくれるOSS。


UIBezierPathでパス描画してUIImageを生成してるので画像ファイル不使用。与えたサイズに対して相対位置で描画するように実装してあるので大きさも自在。


GRButtons


MacのキーボードからiPhoneに文字入力

MacのキーボードからiPhoneとかiPadの文字入力できるようになります。今設定してみたばかりだけど超便利な予感。



MacをWinodwsのキーボードにできるアプリ、1Keyboardが理想的すぎて感動した


ファイルテンプレートの詳細な解説ページ

Xcode4の、ファイルテンプレートまわりの詳細な解説ページ。


TemplateInfo.plistの各要素の説明とか、"___FILEBASENAMEASIDENTIFIER___"とかのテンプレート関連マクロが何に展開されるか等、とにかく徹底解説な感じです。


このあたりはAppleの正式なドキュメントがないので、レア情報かと。



Creating Custom Xcode 4 File Templates


画面の解像度種別を返してくれるカテゴリ

iPhone5の画面サイズ対応についての質問と回答。回答2つめあたりの、UIDevice+Resolutionsっていうenumで定義した画面の解像度種別を返してくれるカテゴリはつくっとくと便利そう。



iOS 6 apps - how to deal with iPhone 5 screen size? - Stack Overflow


AppStoreのレビューをスクレイピングしてDBに保存

AppStoreのレビューをスクレイピングしてDBに保存したり表示したりするツール。


保存したレビューの一覧表示だけじゃなく、★の数を集計してグラフ表示したり、頻出キーワードを抽出したりもできるとのこと。


AppReviewViewer


iOSアプリ開発の細かい仕事で小遣い稼ぎ

仕事をアウトソースできるwebサービス。Skill RequiredをiPhoneに絞ると結構iOSアプリ開発まわりの仕事もでてきます。


アプリまるっと作ってくれ系は安すぎるものが目立ちますが、リンク先はGPUImageの調整をしてほしいというもので、こういう細かい仕事は小遣い稼ぎにはいいかもしれません。(この案件は既にclose)



GPUImage adjustment | iPad | iPhone


ビューにスポットライトをあてる

スポットライトのように、中心をピークに、放射状に徐々に暗くするOSS。addSpotlightInView:atPoint: っていう感じでスポットライトをあてるビューと位置を指定するだけなので直観的。


ソースを見たところ、内部実装としては、CoreGraphics の CGGradientRef を使用して実装している。ちゃんとadd/removeのときにふわっとするアニメーションも実装してある。



MLPSpotlight for iOS - Cocoa Controls


おわりに

今回20個ほどピックアップしましたが、日々ちまちまと投稿しているので、Facebookページの方にはこの10倍ぐらいはTipsがたまってると思われます。


「いいね!」しておくとTipsがタイムラインに流れるようになるので、よろしければどうぞ!


iOSアプリ開発に役立つTips

http://www.facebook.com/iOSDevTips



2013-05-01

UIKit で物理演算エンジンを使用する

cocos2d や Unity などのゲームエンジンや openFrameworks では、標準で物理演算エンジンがサポートされていて手軽に扱えますが、ビューを作成したり画像を表示したりといった基本的な部分の実装方法や、ものによっては使用言語も違うため、「物理演算エンジンを使用したい」というだけの場合はかえって導入障壁が高くなる場合もあります。


それらを使用せず、UIKit ベースでの iOS アプリケーションに物理演算エンジンを単体で導入する方法、すなわち UIView オブジェクトを剛体として物理演算に基づいて動かす方法を紹介します。



準備

1. Box2D のソースをダウンロード

適当なフォルダで、次のように svn の checkout を実行します。

svn checkout http://box2d.googlecode.com/svn/trunk/ box2d-read-only

2. Box2Dをプロジェクトに追加

box2d-read-only/Box2D/Box2D フォルダごと、プロジェクトに追加します。


3. Box2Dのヘッダファイルのパスを登録

Build Settings の "Header Search Path" に

${PROJECT_DIR}

を登録します。


4. 拡張子を変更

Box2D は C++ で書かれているため、Objective-C++ としてコンパイルされるよう、Box2D を使用するクラスファイルの拡張子を .mm にします。


5. ヘッダをインポート

手順4で拡張子を .mm にしたクラスで、ヘッダをインポートします。

#import <Box2D/Box2D.h>

問題がなければ、この時点でビルドに成功します。


物理ワールドと地面の設定

1. メンバ変数を宣言

物理ワールドを保持するためのメンバ変数を宣言します。

@interface ViewController ()
{
    b2World *world;
}
@end

2. 物理ワールドを生成

まず、重力を b2Vec2 型で設定します。

b2Vec2 gravity;
gravity.Set(0.0f, -9.81f);

9.81 は地球表面での重力加速度です。Box2D の座標系が左下を (0, 0) として右上に向かって大きくなるので、重力値は下に向かうという意味でマイナスの符号がついています。


次に設定した重力の値をコンストラクタの引数に渡して b2World 型の物理ワールド(物理演算でシミュレーションする空間)を生成します。

world = new b2World(gravity);
world->SetContinuousPhysics(true);

3. 壁の剛体を生成

画面の上下左右に当たり判定のある壁を生成します。


まず、壁の剛体を b2Body 型として生成します。

// 壁の剛体を定義
b2BodyDef edgeBodyDef;
edgeBodyDef.position.Set(0, 0);

// 壁の剛体を生成
b2Body *edgeBody = world->CreateBody(&edgeBodyDef);

次に、壁の形状を設定します。上下左右に壁を設けるため、形状の型として b2EdgeShape を使用します。


上下左右のそれぞれの辺ごとに両端の座標を設定し、壁の剛体として生成した b2Body オブジェクトに CreateFixture メソッドを用いてセットします。

// 壁の形状を設定
b2EdgeShape edgeShape;
float32 widthByMeter  = screenSize.width  / kPointsPerMeter;
float32 heightByMeter = screenSize.height / kPointsPerMeter;

// 下
edgeShape.Set(b2Vec2(0, 0),
              b2Vec2(widthByMeter, 0));
edgeBody->CreateFixture(&edgeShape, 0);

// 上
edgeShape.Set(b2Vec2(0, heightByMeter),
              b2Vec2(widthByMeter, heightByMeter));
edgeBody->CreateFixture(&edgeShape, 0);

// 左
edgeShape.Set(b2Vec2(0, heightByMeter),
              b2Vec2(0, 0));
edgeBody->CreateFixture(&edgeShape, 0);

// 右
edgeShape.Set(b2Vec2(widthByMeter, heightByMeter),
              b2Vec2(widthByMeter, 0));
edgeBody->CreateFixture(&edgeShape, 0);

上記コードで用いている kPointPerMeter は、「UIKit におけるポイント単位での長さを、Box2D の世界における長さの単位であるメートルに変換する」ための定数で、サンプルでは次のように定義してあります。

#define kPointsPerMeter 16

この定義は、1メートル=16ポイントということを意味しています。


UIViewオブジェクトの剛体を生成

UIView オブジェクト(UIView または UIView を継承するクラスのオブジェクト)に対して b2Body 型の剛体を生成します。ここでは次のように、「引数に渡された UIView オブジェクトに対して剛体を生成する」メソッドを実装します。

- (void)addPhysicalBodyForView:(UIView *)physicalView {
    
    CGSize screenSize = self.view.bounds.size;
    CGPoint pos = physicalView.center;
    CGSize physicalSize = physicalView.bounds.size;

    // 剛体を定義
    b2BodyDef bodyDef;
    bodyDef.type = b2_dynamicBody;
    bodyDef.position.Set(pos.x / kPointsPerMeter,
                         (screenSize.height - pos.y) / kPointsPerMeter);
    bodyDef.userData = (__bridge void *)physicalView;
    
    // 剛体を生成
    b2Body *body = world->CreateBody(&bodyDef);
    
    // 剛体の形状を設定
    b2PolygonShape dynamicBox;
    
    CGFloat boxHalfWidth  = physicalSize.width  / kPointsPerMeter / 2.0;
    CGFloat boxHalfHeight = physicalSize.height / kPointsPerMeter / 2.0;
    dynamicBox.SetAsBox(boxHalfWidth, boxHalfHeight);
    
    // 形状と各種パラメータを剛体にセット
    b2FixtureDef fixtureDef;
    fixtureDef.shape = &dynamicBox;
    fixtureDef.density = 3.0f;
    fixtureDef.restitution = 0.5f;
    body->CreateFixture(&fixtureDef);
}

ここでは形状の型として「多角形の剛体」を定義できる b2PolygonShape を使用し、SetAsBox メソッドで長方形に設定しています。


また引数に渡された UIView オブジェクトと生成する剛体とを結びつけるために、

  • UIView オブジェクトの座標を Box2D の座標形に変換し、b2BodyDef 構造体の position 要素にセットして位置を合わせる
  • userData 要素にUIView オブジェクトのポインタを格納

ということを行っています。


時間を経過させる

物理演算に基づいて剛体を動かすには、物理ワールドの時間を進める必要があります。


時間管理用のNSTimerを生成します。

@property (nonatomic, assign) NSTimer *timer;

self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0/60.0
                                              target:self
                                            selector:@selector(step:)
                                            userInfo:nil
                                             repeats:YES];

ここでは60FPSで動作するようタイマーを生成しています。


時間が1/60秒経過するごとに呼ばれるメソッドを次のように実装します。

- (void)step:(NSTimer *)timer {
    
    // 物理ワールドの時間を進める
    int32 velocityIterations = 8;
    int32 positionIterations = 1;
    
    world->Step(1.0f/60.0f, velocityIterations, positionIterations);
    

    // UIViewオブジェクトを剛体に合わせて移動、回転させる
    CGFloat screenHeight = self.view.bounds.size.height;
    for (b2Body *aBody = world->GetBodyList(); aBody; aBody = aBody->GetNext())
    {

        if (aBody->GetUserData() == NULL) {
            
            continue;
        }
        
        // 剛体のuserDataに格納されているUIViewオブジェクトへのポインタを取得
        UIView *aView = (__bridge UIView *)aBody->GetUserData();
        
        // 剛体の現在位置に合わせUIViewオブジェクトを移動させる
        b2Vec2 bodyPos = aBody->GetPosition();
        CGFloat newCenterX = bodyPos.x * kPointsPerMeter;
        CGFloat newCenterY = screenHeight - bodyPos.y * kPointsPerMeter;
        aView.center = CGPointMake(newCenterX, newCenterY);
        
        // 剛体の現在の角度に合わせUIViewオブジェクトを回転させる
        CGAffineTransform transform;
        transform = CGAffineTransformMakeRotation(-aBody->GetAngle());
        aView.transform = transform;
    }
}

物理ワールドの時間を経過させ、その経過によって生じた剛体の位置と向きの変化を、剛体に結びつけられた UIView オブジェクトに反映させています。


タップで剛体を生成

ビューをタップしたら物理演算に基づいて動作するUIViewオブジェクトを生成するようにします。


次のように UITapGestureRecognizer オブジェクトを生成し、ビューに登録します。

UIGestureRecognizer *gesture;
gesture = [[UITapGestureRecognizer alloc] initWithTarget:self
                                                  action:@selector(tapped:)];
[self.view addGestureRecognizer:gesture];

そしてタップジェスチャーのハンドラメソッドを次のように実装します。

- (void)tapped:(UITapGestureRecognizer *)gesture {
    
    CGRect frame = CGRectMake(0, 0, 50, 30);
    UIView *phisicalView = [[UIView alloc] initWithFrame:frame];
    phisicalView.backgroundColor = [UIColor colorWithRed:0.0
                                                   green:204.0/255.0
                                                    blue:1.0
                                                   alpha:1.0];
    [self.view addSubview:phisicalView];
    
    CGPoint tappedPos = [gesture locationInView:gesture.view];
    phisicalView.center = tappedPos;

    [self addPhysicalBodyForView:phisicalView];
}

UIViewオブジェクトを新たに生成し、手順『UIViewオブジェクトの剛体を生成』で実装した addPhysicalBodyForView: メソッドでその UIView オブジェクトに対して剛体を生成しています。

以上の手順で、画面をタップすると物理演算に基づいて動く UIView オブジェクトが生成されるようになります。


ワールドの解放

生成した物理ワールド(b2World インスタンス)が必要なくなったら、delete で解放します。

delete(world);

ワールドを解放すると、CreateBody や CreateFixture で生成された剛体やフィクスチャも解放されます。


剛体やフィクスチャを個別に解放したい場合は、それぞれ DestroyBody, DestroyFixture を使用します。


参考資料


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 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2016 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 11 | 12 |
2017 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2018 | 02 |