2011年2月28日月曜日

UIWebView のスクロールを制御するためのプロパティを書いてみた

UIWebView にどうして scrollEnabled プロパティがついてねえんだ Apple のチンパンジー野郎!とお嘆きの全国1000万の iOS 開発者の皆様、こんばんわ。もちろん私もその一人であります。

嘆いていてもしょうがないので何とかスクロールを制御する方法を・・・と思って探していたら、すでに2009年の地点で @nakamura001 さんがこんなブログを書いてらっしゃいました。

http://d.hatena.ne.jp/nakamura001/20090520/1242837408

が、遷移先で詳解されている

http://praveenmatanam.wordpress.com/2009/04/03/how-to-disable-uiwebview-from-user-scrolling/

のコードが正直いまいちなのです。何がいまいちって、せっかくのCocoa環境であるにも関わらず、わざわざobjc/runtime.hなんていう低レベルなC言語の関数を使っています。別にパフォーマンスがタイトな場所でもないですし、かっこよくCocoaっぽく書き直してみました。ということで書き直したコードがこちら↓

https://gist.github.com/846258

で、このAdditionを導入するとですね、
UIWebView *webView = [[[UIWebView alloc] initWithFrame:frame] autorelease];
webView.webViewScrollEnabled = NO;
みたいな書き方ができてハッピーになれます。

内部的には NSInvocation を使っています。 Mac OS X 10.5 (iOS 2.0) から存在するこのクラス、本当に便利で、ぶっちゃけ呼び出し対象のシグネチャさえわかれば何でも呼び出せるスグレモノです。フレームワークを作るときなど、呼び出し先のシグネチャしかわからない状況下で対象のメソッドを呼び出す時などに便利な感じかもです。その上 NSInvocationOperation を使ってそのまま並列化もできたりして。
NSMethodSignature *sig = [subview methodSignatureForSelector:selector];
NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:sig];
[invocation setTarget:subview];
[invocation setSelector:selector];
[invocation invoke];
BOOL result = YES;
[invocation getReturnValue:&result];
return result;

2011年2月20日日曜日

github で pull request をされたとき・するときの手順

github に自分のリポジトリを公開していると、たまに pull request をされることがあります。また逆に、他人のリポジトリのコードを使っていて、どうしても気になるバグを見つけて修正したときなど、相手に pull request を送りたいことがあります。こんなときにどうすればよいかをまとめてみました。


■pull request をしたいとき

pull request をしたいときは、まず相手のリポジトリを fork する必要があります。


このボタンをぽちっとな。

fork したら、 fork して自分の管理下に入ったリポジトリを clone して、コードを修正します。
git clone https://akisute@github.com/akisute/asi-http-request.git
コードの修正が終わったら、自分の fork したリポジトリに push しておきます。
git push origin master
ではコードの修正が終わったので pull request をします。


このボタンをぽちっとな。


するとこうなります。デフォルトでは相手の master に対して自分の master を pull request することになりますが、もちろんこの画面で変更可能です。 request をする先のリポジトリ・ブランチと、リクエストされるブランチをそれぞれ指定できます。それからコメントをつけて送れます。ここでつけたコメントは相手側のリポジトリの Pull Requests タブに記載されるので、それなりにかっこいい内容を書いておくと良いかと思います。あとは相手が受け入れてくれるのを待つばかりです。自分のコメントにコメントがついたらメールとかで通知が来ますんで、適当に返事して相手と連絡を取りながらやる感じになります。


■pull request をされたとき

逆に自分が pull request をされることもありますので、そのときの対処法も。


pull request されると、こんな風に Inbox の Notifications の中に通知が来ます。


対象のリポジトリの Pull Requests タブにもこんな具合でリクエストが入ります。


クリックしてリクエストの詳細を開きます。ファイルの diff 一覧とかどっからどこに対してリクエストが飛んでいるのよとか全部ここで見られます。相手のコメントに対してコメントを返したり、 diff に対してコメントをつけたりできます。 diff につけたコメントは相手にも公表されます。

ここで相手とコメントやりとりとかして、じゃあ取り込もうか、となりましたら、実際に pull request を取り込む作業を行います。自分のリポジトリにマージ用のブランチを用意して、 pull して確認し、問題なければマージ用のブランチと本線をマージして終了。コマンドがよくわからなくても github が全てのコマンドを表示してくれるのでそれを見れば一発です。本当に親切。以下、 master ブランチに対して pull request があったとすると、
git checkout -b pull-request-master master
git pull http://github.com/applicant/his-repository
# 相手のコミットが自分のブランチに pull されてくるので、この状態で動作を確認する
# merge 作業が発生するならここで行う
# 問題がなさそうなら
git commit
git merge master
git push origin master
push まで完了したら先ほどの pull request のページから取り込み完了したよボタンを押しておきましょう。


■おまけ: 自分が fork しているコードの fork 元リポジトリが更新されたので、こちらも最新にしたい

実はこれは pull request をされたときとあんまり変わりません。対象の相手のブランチを自分の fork しているコードに pull すればよいのです。
git checkout -b merge-master master
git pull http://github.com/parent/parent-repository
# 相手のコミットが自分のブランチに pull されてくるので、この状態で動作を確認する
# merge 作業が発生するならここで行う
# 問題がなさそうなら
git commit
git merge master
git push origin master

2011年2月12日土曜日

Redmine を Debian 5.0 にゼロからインストールしてみた

あらかじめ注意: 下記のメモは、ruby, gem, rails, rack, redmineのいずれかのバージョンが0.0.1でも違うと動作しない可能性が高いので、当てにしないでください。


■顧客が説明した要件
  • 1つのサーバーに複数のプロジェクトのリポジトリとかリソースをおいているので、1プロジェクトにつき1個インスタンスが必要なtracは都合が悪い!
  • っていうかtrac飽きた!!古い!
  • 複数プロジェクト扱えるBTSが欲しい
  • 見た目クールだといいね、カスタマイズとかできるとすごくいい!
  • Wikiは絶対必須
  • Google Appsと連携できると素敵だな!
  • 簡単にインストールしたい、時間かけたくない


■プロジェクトリーダーの理解
  • https://www.gravitydev.com/ はすごくいいんだけれど許可下りなさそうだなぁ。そもそもWikiがないから却下。
  • となるとやっぱりRedmineかな!あれすごくいいよね!
  • Redmineに移行しようかみたいな話も社内で出てたし、先んじて導入してノウハウ作るのはありだな!


■アナリストのデザイン
[11/02/10 9:24:53] akisute: redmineのインストールが超面倒くさいんだけれど
[11/02/10 9:24:54] akisute: なんとかならんのこれ
[11/02/10 9:25:02] wozozo ( ゚Д゚)y─┛~~: rails
[11/02/10 9:25:19] tokibito: rails
[11/02/10 9:25:20] 文 殊堂: まいんちゃん
[11/02/10 9:26:39] akisute: http://redmine.jp/guide/RedmineInstall/
[11/02/10 9:26:43] akisute: これ見てるとアホかと言いたくなってくる
[11/02/10 9:26:50] akisute: Ruby 1.9には対応していません。 上記の表に示したバージョンのRuby 1.8.xをインストールしてください。
RubyGems 1.3.1以上が必要です。
Rake 0.8.3以上が必要です。
Rack 1.0.1が必要です。1.0.1がインストールされていない場合、データベースマイグレーションが失敗します。
[11/02/10 9:27:25] wozozo ( ゚Д゚)y─┛~~: aptでいれたgemをupdateしようとすると、
[11/02/10 9:27:34] wozozo ( ゚Д゚)y─┛~~: おい待てって言われるし
[11/02/10 9:27:49] aodag: Redmineってライトニングなのないの?
[11/02/10 9:27:53] tokibito: rubyはaptで入れたら死亡する可能性
[11/02/10 9:28:04] aodag: rvmっしょ
[11/02/10 9:28:04] tokibito: 自分でビルドしたほうが無難とかいう
[11/02/10 9:28:11] tokibito: rvmで。


■プログラマのコード
  1. redmine:redmine ユーザーを新規に作成する
    • 以下の作業はすべてredmineユーザーで行う
  2. rvmの最新をインストールする
    • http://rvm.beginrescueend.com/rvm/install/
    • curl と git-core のインストールが必要。aptitude経由で実施
    • /home/redmine/.bashrcの追記を忘れないように
    • source ~/.rvm/scripts/rvm も忘れないように
  3. rvmに必要なパッケージをインストールする
    • こんなんデフォルトで用意しろよ・・・いいかげんにしろ・・・
    • rvm package install readline
    • rvm package install iconv
    • rvm package install zlib
    • rvm package install openssl
    • この順番で入れないと依存関係の問題で怒られます (opensslがzlibを使うとか)
  4. ruby 1.8.7 をrvm経由でインストールする
    • 参考は http://d.hatena.ne.jp/masasuz/20100720/1279635055
    • 先に rvm package install しないとgemが使えない, zlibがないので
    • ビルドオプションを付けることでopensslとzlibを有効にすること
    • rvm install 1.8.7 -C --with-openssl-dir=$HOME/.rvm/usr -C --with-zlib-dir=$HOME/.rvm/usr
    • rvm use 1.8.7
    • ruby --version
    • gem --version
  5. gemのバージョンを 1.3.7 に戻す
  6. 本当にopensslとかzlibとかインストールされているかをチェックする
    • ruby -r openssl -e ""
    • エラーになった場合はopensslの再ビルドを行う。opensslを使えるようにしないとのちのちrakeを実行するところでエラーが発生して先に進めなくなる
    • cd ~/.rvm/src/ruby-1.8.7-p330/ext/openssl
    • ruby extconf.rb --with-opt-dir=$HOME/.rvm/usr/
    • make
    • make install
    • ruby -r openssl -e "" を再度実行してエラーにならない事を確認
  7. rails 2.3.5 をgem経由でインストールする
    • gem install rails -v=2.3.5
    • rails 2.3.5をインストールすると自動的にrack 1.0.1が入るが、先にrack 1.0.1をインストールしておいたほうが良いかも
  8. rack 1.0.1 をgem経由でインストールする
    • gem install rack -v=1.0.1
  9. sqlite3-ruby 1.2.3 をgem経由でインストールする
    • 参考は http://d.hatena.ne.jp/modka/20101208
    • sqlite3-devel (sqlite3.h) が見つからないとインストール出来ないので注意
    • 最新のsqlite3-ruby は sqlite3 3.6.3 が必要で、Debianのaptitudeでは3.5.9までしか無いので、バージョンを指定して古いsqlite3-rubyを入れる
    • sudo aptitude install sqlite3
    • sudo aptitude install libsqlite3-dev
    • gem install sqlite3-ruby -v=1.2.3
  10. i18n 0.4.2 をgem経由でインストールする
    • rakeの実行に必要
    • rakeお前依存関係に含めろや・・・アホか・・・><
    • gem install i18n -v=0.4.2
  11. redmine のリリース版の最新をダウンロードする
  12. データベースの設定を行う
    • 参考は http://redmine.jp/guide/RedmineInstall/
    • 今回はsqlite3
    • cd ~/redmine-1.1.1/config
    • cp database.yml.example database.yml
    • vim database.yml
    • cd ~/redmine-1.1.1
    • rake generate_session_store
    • rake db:migrate RAILS_ENV="production"
    • rake redmine:load_default_data RAILS_ENV="production"
  13. メールの送信設定を行う
    • 不要と思われる。メールが飛ばないだけで済む
  14. 起動する
    • cd ~/redmine-1.1.1/
    • ruby script/server webrick -e production
    • http://localhost:3000/ が立ち上がるので、 admin/admin でログインする
    • 適切にadminのパスワードを変更すること
  15. ログの設定を行う
    • やっとかないとログが溜まりまくって死ぬらしいので
    • cd ~/redmine-1.1.1/config
    • cp additional_environment.rb.example additional_environment.rb
    • vim additional_environment.rb
    • http://redmine.jp/guide/RedmineInstall/ のログの設定セクションのとおりに設定しておく
  16. Passengerをインストールする
    • 参考は http://d.hatena.ne.jp/Umeyashiki/20100131/1264948069
    • gem install passenger
    • passenger-install-nginx-module
    • ここまでの手順どおりに進めていると、大体以下のものが足りないはず
      • Curl development headers with SSL support... not found
      • OpenSSL development headers... not found
      • Zlib development headers... not found
    • 一旦abortしてそれぞれインストールすること
      • sudo aptitude install zlib1g-dev
      • sudo aptitude install libssl-dev
      • sudo aptitude install libcurl4-openssl-dev
    • nginxの再ビルド(ないしソースコード)が必要になるので、すでにapt-getしたnginxがある場合には使えない。その場合はpassengerをstandaloneで起動して、そこに対してnginxからproxy-passなりなんなりする。
    • 以下、passengerスタンドアロンで起動し、redmineはすべてpassengerにやらせる場合を考えます。ただしpassengerのスタンドアロンは内部的にはnginxを使っているので、いずれにせよ上記のインストールは必要になります。
    • cd ~/
    • mkdir log run
    • passenger start redmine1.1.1 -d -a 0.0.0.0 -p 3000 -e production --log-file=log/passenger.log --pid-file=run/passenger.pid
    • ってやると何故か/tmp以下にlogファイルをつくろうとしていみわからないエラー吐いて死ぬので中止
    • cd ~/redmine1.1.1
    • passenger start -d -e production
    • 最低限のオプションだけで起動すれば起動できた


■営業の表現、約束
  • お客様、 redmine をお求めですか?ご安心ください! gem install redmine で一発に決まってますよ!簡単です!
    • そんなものはない
  • それなら sudo apt-get install redmine で一発です!猿でもインストールできます!!
    • Debian では無理
  • それなら最初から redmine がインストールされている vm イメージがあります!こちらを使えば一発です!
    • Ubuntu と SUSE しかない
  • Trac Lightning とかゆとり。真のエンジニアは ruby の configure & build から自分でやって当然(キリッ
    • そんな業務と関係ないところに何時間(何万円)使うつもり?
    • アップデートどうすんの?何時間(何万円)使うつもり?


■プロジェクトの書類

http://redmine.jp/guide/


■実装された運用

http://www.redmine.org/projects/redmine


■顧客への請求金額

akisute の実働5時間分 = 数万円 = https://www.gravitydev.com/pricing のスタンダードプラン数年分


■得られたサポート

http://redmine.jp/guide/RedmineInstall/


■顧客が本当に必要だったもの

http://twitter.com/shin_no_suke/status/35632371084562433
http://twitter.com/hirokinko/status/36307730025156608
http://twitter.com/hirokinko/status/36310634127695872


■元ネタ

http://ryonoda.wordpress.com/2008/09/26/


■一応補足

Redmine自体はすごく良いアプリだと思います。ええ。いま使ってますが、とっても助かってます。

bitnamiの配布しているパッケージが対話環境でもインストールできるというのは知りませんでした。っていうかbitnamiのページのどこにも書いてないんですが・・・><
wget http://bitnami.org/files/stacks/redmine/1.1.1-0/bitnami-redmine-1.1.1-0-linux-installer.bin
chmod +x bitnami-redmine-1.1.1-0-linux-installer.bin
./bitnami-redmine-1.1.1-0-linux-installer.bin --mode text
でうまくいくようです。

しかしこの方法を使うと勝手にApache, MySQL, Ruby, Subversionをインストールされてしまうみたいなので、きちんと管理したいとなるとちょっと不向きかなと。

2011年2月9日水曜日

UITableView の cell の中に UITextView を配置したとき発生する問題とその対処方法

たとえば Twitter クライアントや SMS クライアントのように、短い文章を入力させるような画面を作りたいとき, Grouped Style な UITableView の cell の中に UITextView を配置して画面を作る事が良くあるかと思います。通常、この作りでほとんど問題はありませんが、特定の条件下で問題が発生することがわかりましたので、回避策をメモしておきます。


■問題

この問題が発生するのは、以下の条件を満たしたときです。
  1. UITableView の cell の中に UITextView を配置する。
  2. UITextView に、縦スクロールが発生するぐらい長い文字列を入力する。
  3. UITextView の中ほどにカーソルを移動する。
  4. UITableView と UITextView の scrollEnabled プロパティを NO に設定する。
  5. UITextView の text プロパティを変更する。
すると, UITextView および UITableView の scrollEnabled プロパティを NO に設定しているにもかかわらず、勝手に UITableView がスクロールを起こしてしまいます。 UITextView の text プロパティを書き換えたら勝手に UITextView が一番下までスクロールしてしまうのはデフォルトの挙動であり、これは問題ありません. UITextView を単品で使っているときは, scrollEnabled プロパティを NO に設定することでスクロールを発生させずに text を書き換えることが可能なのですが, UITextView を UITableView の中に含めたときのみ, scrollEnabled プロパティが無視されてしまい、この問題が発生してしまうようです。

カスタムキーボードを用意したり、本文中のURLを短縮して差し替えたりなど、結構 UITextView の text プロパティを直接書き換えたい要件があったりするので、このままでは困ります。


■解決方法

そこで、相当な荒技ですが、以下のようにして解決を図ることが可能です。
  1. UITableView の cell の中に UITextView を配置する。
  2. UITextView をいったん removeFromSuperview する。
  3. UITextView をいったん UITableView 以外の場所に addSubview する。
    • UIWindow の直下などがおすすめ。
  4. UITextView の scrollEnabled プロパティを NO に設定する。
  5. UITextView の text プロパティを変更する。
  6. UITextView の scrollEnabled プロパティを YES に設定する。
  7. UITextView を元の view に addSubview する。
  8. UITextView を first responder にする。
コードにすると以下のようになります:
UIView *parentView = textView.superview;
[self.view.window addSubview:textView];
textView.scrollEnabled = NO;

textView.text = [currentText stringByReplacingCharactersInRange:selectedRange withString:shrunkenURLString];
textView.selectedRange = NSMakeRange(selectedRange.location + [shrunkenURLString length], 0);

textView.scrollEnabled = YES;
[parentView addSubview:textView];
[textView becomeFirstResponder];
こうすることで, UITableView が勝手にスクロールしてしまう問題を回避することが可能です。

2011年2月4日金曜日

TestFlight 経由のアプリのダウンロード速度を調べてみた




最近 iOS 界隈を賑わせている TestFlight というWebサービスがあります。詳細についてはまぁ TechCrunch さんでも読んでいただくとしまして、この TestFlight が非常に便利そうなので私のほうでも早速試してます。登録も簡単、アプリのマネジメントも良くできていますし、チームを複数用意して管理できるというのも良いです。なんで Apple がデフォルトでこれを用意してくれないのかというぐらい便利です。

が、一つだけ気になったことがあって、まぁタイトル通りなのですが、TestFlight経由だとアプリのダウンロードが遅いのではないか?ということです。ということで実際にやってみました。


■条件

対象のアプリのサイズは17.7MB。
以下の2カ所の回線で計測しました。
  • 会社の回線(確かバッファロー, IEEE 802.11g, だいたい300KB/s程度)
  • 自宅の回線(AirMac Extreme, IEEE 802.11n, だいたい1MB/s程度)

■結果

会社の回線・・・ダウンロードに12分~15分程度
自宅の回線・・・ダウンロードに1分前後、インストール完了まで2分ちょっと

それぞれ別の日に計測したので何とも言えませんし、会社の回線は自分以外の人も使うので遅いときがあるのですが、およそ

遅いとき・・・20KB/s程度
早いとき・・・300KB/s程度

とまぁずいぶん幅がある結果になってしまいました。どうやら時間帯や相手のサーバー側の混雑具合によっても速度が違ってくるようです。いずれにせよそんな猛烈に速いわけではないので注意が必要そうです。たとえば100MBぐらいあるゲームアプリをテストしたいときなどは、今まで通りの地味な配布のほうがよい・・・ということもあるかも。


■余談:TestFlightってどれぐらいのファイルサイズまでアップロードできるのか?

と思って調べてみたらありました。500MBだそうです。400MBのファイルを実際にアップした人がいるみたいです。
http://support.testflightapp.com/discussions/questions/60-max-file-size-on-build-upload

2011年2月2日水曜日

"Failed to launch simulated application: Unknown error." が発生したときの対処法



iPhone シミュレーター起動時に表題のエラーが発生したときの対処法です。


表題のエラーは文字通り原因不明の問題が発生したときに表示されるのですが、問題のうちの一つに「バンドルリソース内にMac OSが使用する予約されたリソース名が含まれていると発生する」というものがあります。

たとえば
Contents
Resources
などの名前のディレクトリやファイルをバンドルリソース内に作成してしまうと、ビルドは通るのですがシミュレーター実行時にエラーが発生するようです。これらの予約された名前は使わないようにすると良いです。

2011年2月1日火曜日

iPhone iPad 向けスタイラスで書き味勝負をしてみた


写真は上から順に、 パワーサポート スマートペン PBJ-9XシリーズPogo SketchoStylus 初期限定生産型


2回戦の記事はこちら: http://akisute.com/2011/05/iphone-ipad-2.html
おすすめスタイラスまとめ記事はこちら: http://akisute.com/2010/06/ipad.html
Bamboo Stylusのレビューはこちら: http://akisute.com/2011/05/ipad-bamboo-stylus.html
oStylusのレビューはこちら: http://akisute.com/2010/10/ipad-ostylus.html

このたび自分が納得できる完成度の iPad 向けスタイラスが3種類揃ったのを機会に、ちょっと書きくらべをしてみる事にしました。


■さっそく試してみた

以下、実際に手書きで字を書いてみたスクリーンショットを貼り付けます。全て一発勝負で書いています。それぞれ上からスマートペン、Pogo Sketch、oStylusです。

neu.Notes



Penultimate




■考察

スマートペンは本当にまるで普通のペンで紙に書いているように書けます。特に「返」のしんにょうあたりを見るとよくわかるのですが、このような綺麗な曲線が非常に楽に書けます。ただしやや太いためか細かい筆跡が難しいです。

Pogo Sketchはスマートペンより細く、やや滑りが悪いため細かい線に強いですし、曲線もしなやかです。しかしながらよく見ると所々線が途切れていたり短かったりすることがわかると思います。これはタッチの感度が悪いためで、タッチを認識せず線が途切れてしまっているためこのようになってしまっています。

oStylusは感度の良い金属製のペン先とペン先が見えるOリング構造のおかげで、細かい字に圧倒的な強さを誇ります。Pogo Sketchのように感度が足りず線が途切れることもありません。弱点として、全体的に長く重くペン先が平たいため、普通のペンと同じようには書けず、線が直線的になってしまっています。

一番書くのが楽だったのはスマートペン、書くのに時間がかかっているのはoStylusだったと思います。


■まとめ

それぞれ一長一短、特徴ある iPad / iPhone 向けのスタイラスが市場に出回るようになってきて、すごくわくわくする感じです!つい1年2年前まではろくな商品がないと嘆いていたのですが、いまや自分の書きたい物に応じてスタイラスを選ぶことさえできるようになりました。素敵!