久しぶりのブログ。たまにはマトモな技術ネタ書くよ。
画像で作るの大変なので、パラメータいじっただけでできないかなーと調べてたところ、
このサイトにあるUIButtonの背景設定方法、
おぉ!と思ったけどちょっと惜しい。
navigation barの色を指定しただけで一発でボタンの色も決まって欲しいなぁと思って考えてたところ、
alphaを上手く使うアイディアを思いついて試してみた。
UIGradientButton.h
#import <Foundation/Foundation.h>
#import <QuartzCore/CAGradientLayer.h>
@interface UIGradientButton : UIButton {
CAGradientLayer *gradientLayer;
}
@property (nonatomic, retain) CAGradientLayer *gradientLayer;
@end
UIGradientButton.m
#import "UIGradientButton.h"
@synthesize gradientLayer;
- (id)initWithFrame:(CGRect)aRect
{
self = [super initWithFrame:aRect];
if (self) {
CGRect gradientFrame = [self bounds];
gradientFrame.size.height = self.bounds.size.height / 2;
gradientLayer = [[CAGradientLayer alloc] init];
[gradientLayer setBounds:gradientFrame];
[gradientLayer
setPosition:CGPointMake(
gradientFrame.size.width / 2,
gradientFrame.size.height / 2
)
];
[[self layer] insertSublayer:gradientLayer atIndex:0];
[[self layer] setCornerRadius:8.0f];
[[self layer] setMasksToBounds:YES];
[[self layer] setBorderWidth:1.0f];
}
return self;
}
- (void)dealloc
{
[gradientLayer release];
[super dealloc];
}
- (void)drawRect:(CGRect)rect
{
UIColor *highColor = [UIColor
colorWithRed:NAVIGATION_COLOR_R
green:NAVIGATION_COLOR_G
blue:NAVIGATION_COLOR_B alpha:0.5
];
UIColor *lowColor = [UIColor
colorWithRed:NAVIGATION_COLOR_R
green:NAVIGATION_COLOR_G
blue:NAVIGATION_COLOR_B alpha:0.9
];
[gradientLayer
setColors:[NSArray
arrayWithObjects:(id)[highColor CGColor],
(id)[lowColor CGColor], nil
]
];
CGContextRef contextRef = UIGraphicsGetCurrentContext();
CGContextSetRGBFillColor(
contextRef,
NAVIGATION_COLOR_R,
NAVIGATION_COLOR_G,
NAVIGATION_COLOR_B,
1.0
);
CGContextFillRect(
contextRef,
CGRectMake(
self.bounds.origin.x,
self.bounds.origin.y + gradientLayer.frame.size.height,
self.bounds.size.width,
self.bounds.size.height - gradientLayer.frame.size.height
)
);
[super drawRect:rect];
}
@end
おぉ!想像以上に奇麗に動くよ、これ!
NAVIGATION_BAR_R、NAVIGATION_BAR_G、NAVIGATION_BAR_B には
navigation bar に指定した色を設定すればOK。
これから重宝しそうだな。
大量データの一覧を表示する際に、PostgreSQLでoffset値が大きくなると、
処理が遅くなってしまう問題が出てきていたので、古典的な手法で解決してみた。
pack(“N”) でIDを4バイトにした連続のファイルを作る。
hoges = Hoge.find(
:all,
:order => "id"
)
f = open("index", "wb")
for hoge in hoges
f.print([hoge.id.to_i].pack("N*"))
end
f.close()
としてindexを作ってやる。
一覧として表示してやる時は、
limit = 100
s = File::stat("index")
count = s.size.to_i / 4
current_page = 1
if params[:page] =~ /^\d+$/
current_page = params[:page].to_i
end
file_offset = s.size - (current_page - 1) * 4 * limit - 4
f = open("index", "rb")
limit.times do
if file_offset < 0
break
end
f.seek(file_offset, IO::SEEK_SET)
pack_id = f.read(4)
unpack_id = pack_id.unpack("N*")
id_array.push(unpack_id[0].to_i)
question_array.push("?")
file_offset -= 4
end
f.close()
@hoges = Hoge.find(
:all,
:conditions => ["id in (" + question_array.join(",") + ")"] + id_array,
:order => "id desc"
)
としてやればoffsetを使わずに高速に降順に引けるようになる。
あとはmodelのhoge.rbに
after_create :make_index
def make_index
f = open("index", "a+b")
f.print([self.id.to_i].pack("N*"))
f.close()
end
としてやればOKだ。
普通に考えるとSQLのチューニングとかになるんだろうけど、
工夫すれば色んなやり方あるよね。
TLに「詳しい情報求む」みたいなtweetが多かったので私見を。
詳しく調べたわけじゃないから正確な情報ではないかもしれないけど、
おそらくtwitterでURLをリンクにする際のaタグのhrefの閉じの「”」ないしは「’」を、
特定の記述をすることでtweetから書けてしまって、
その後のaタグの中身にonmouseoverやonclickなどのjavascriptのイベント処理を、
tweetで追加して書けてしまっているのだと思う。
そのため、マウスをリンクの上に置いた時点などで、
なんらかの処理をさせるということができてしまう。
自動で適当な文を投稿したり、背景色を変えたりするものなどは、
ログインしている情報などを盗んでいるわけではなく、
tweetの内容を記述するtextareaに文書を入れてsubmitしているだけのものだったり、
背景の色を変える処理を追加している程度だと思われるので、
情報を盗まれるなどの被害は起こらず、TLが流れれば被害もなくなる一過性のものだと思う。
ただ、javascriptで色々処理を書いている人はピンと来たかも知れないけど、
これを利用してもっと悪質なことができてしまうはずなんだよね。
(具体的には書かないけど)
サービス側での修正はそれほど難しいものではないと思うので、
沈静化するまで落ち着いて待っていたいと思う。
おそらく問題は明確だから、修正さえ完了すれば安心して使えるだろうし。
iPhoneのアプリのデザインやUIはもちろんだけど、
Twitterからの受け皿になるページなど、
iPhoneのWebページのUIも、もうちょっとどうにかしたいなーと思い始めている。
javascriptのライブラリも出回っているけど、
実は最初はライブラリを使用することには否定的だった。
ファイルサイズがネックになると思っていたから。
アプリのパフォーマンスチューニングをするとわかるけど、
一番の問題は3G回線での利用時の通信速度が遅いこと。
データ転送量を如何に減らすかが大事になってくる。
ライブラリを使用していると、どうしても余計な機能もついてきてしまうので、
その分のファイルサイズを転送しなければならないので難しいなと思っていたわけ。
念のため一応調べてみると、jQTouchなんかは思いの他ファイルサイズが小さかったけど、
jQueryが必須になるので、このファイルサイズがちょっと…と悩んでいたところ、
あることを思い出した。
もともと通信量を抑えるために、Apacheのmod_deflateで通信を圧縮しているのだ。
(iPhoneでサーバーとのデータのやりとりをする場合には絶対入れた方が良い!)
そこで圧縮後の転送量を測ってみると…CSSやjQueryを含めて30KB弱。
これならなんとかなるね!と使用することに決めた。
ちょっと使ってみると非常によく出来ていることがわかる。
デフォルトのテーマのデザインだと物足りなかったので、
テーマをちょっといじった30min.用のテーマを作成すると、
iPhoneアプリライクな動作が簡単に実現できる。
ただ一つだけ問題がでてきた。
UINavigationControllerのようなスライドインのアニメーションを実装するには、
HTML1枚に各種動作を書かねばならないようだ。
(勉強不足でやり方知らないだけだったらすみません。。。)
そうするとHTMLのファイルサイズが大きくなる。
…どうしようか?
とりあえず最初のHTMLに大枠だけ書いて、
スライドイン(ドリルダウン)した時の内容はAjaxでjson読みに行って、
それでコンテンツのタグ生成みたいなことをすれば通信量は減らせるかな?
解決できそうな問題な気がする。
結論としては、Apacheのmod_deflateなど、通信を圧縮することができる環境化では、
jQTouchはかなり実用的だと思います。
O’REILLYの「iPhoneアプリケーション開発ガイド」などを参考にすると導入が楽です。
ただこの本のタイトルは…。
iPhoneアプリを作るものではなくてiPhone用Webサイトを作るための内容なので、
App Storeにアプリを並べたいという人には用途が違うかと。。。
でも、目的に合致した方には良い入門書だと思います。オススメですよ。
昨日は誕生日だったが…。
晩御飯は週末の残り物のカレー。
期待していなかったけど、ちょっとガックリしながら、
誕生日くらい美味しいものにしようかと山手ラーメンに行きたい気持ちを抑えつつ食す。
今日の昼。奥さんから「誕生日おめでとう!」メールが届く。
本当は昨日であると言いたいけど、波風立てぬつもりで「ありがとー」とメールを返す大人な僕。
そこで一つある問題が。
昨夜、奥さんの居る前で、「明日20時から2人で予約をお願いします」と、
行きつけの知人のお店に電話を入れていたのだ。
…いや、僕は誕生日はちゃんと家に帰ったよ!
でも、奥さんの思っている僕の誕生日は今日だ。
なぜ2人で出かける?と思われても仕方がない。
一緒に飲んでいたのが学生時代柔道で名を馳せた、いかつい男の子だとは知るよしもない。
善意で「ありがとー」と言ったけど、もしかしてヤバイ?
…帰宅するとトイレに新婚旅行の時に買ったカエルの絵が飾ってあった。
うーん。
何か言うべきか、言わぬべきか?
他人の作ったシステムの移植や、
途中からヘルプで入る案件を経験して「?」と思うことはないだろうか?
- 環境やある条件に依存するコードが直書き
- 変数名と違う内容が入っている変数
- コピペによる二重化
- 他プロジェクトのファイルのコピーによる無駄な変数
一つずつ考えて行こう。
まず「環境やある条件に依存するコードが直書き」。
そういうコードを最初から書いてあればあきらめるのだけど、
共通変数や環境に依存する処理を取りまとめているクラスファイルなどがあって、
移植する時はここを直せば行けますよーと言われつつ…ダメじゃん、みたいなこと。
事前に聞いていた話から見積もるコスト通りに行かず、
どこが原因なのか調査、修正が必要になって、実際の作業量に大分差が出てくる。
「変数名と違う内容が入っている変数」
これも結構悩む。人のコードに手を入れる時に、
変数名を見ながら直感的にこれは○○だと思っていたら、実は全然違うもの。
name って書いてありながら年齢が入っていたりとか…。
おそらく何かしらのコードのコピーか流用で、変数名をそのまま使ったんだろうけど、
後で読む時には非常に困る!せめて変数名くらいは修正して欲しいなぁと。
「コピペによる二重化」
「ここのファイルにこういう処理書いてあるから、同じようにすれば動くよ。」
と言ってみたところ、その処理をコピーして違うコードに貼ってしまうこと。
でもそこに将来修正が入るだろうと予測できる場合、
共通処理を書き出してどこか一箇所にまとめておいておくべき。
後日修正した際に、「えっ、そっちも直さなくちゃいけないの?」ということを無くしたい。
その場凌ぎで急ぎで対応してしまう気持ちもわからなくはないけど、
将来のミスの可能性を未然に摘み取っておきたい。
「他プロジェクトのファイルのコピーによる無駄な変数」
これも以外に困る。「この変数は何に使っているんだろう?」と悩んでしまう。
定義してあるけど使っている場所がわからないとか。
さらにその変数が重要な意味を持つ名前だともっとことが深刻だ。
全部のファイルをgrepして変数名を探してみて、
使ってないことを確かめた後意味がないものとわかってコメントアウトしてみて、
一通り動作検証した後に削除。
こんな無駄な手順を踏みたくないので、
全くプロジェクトに関係ない変数や処理は、コピーして来た時点で削除しておいて欲しいところ。
いくつか挙げてみたけど、
どれもひと手間かければ簡単に防げること。
そういうところで問題が出るとすごくもったいないな、と思う。注意すれば防げたことだから。
あと、不思議とこういうモラルのないコードは伝染しやすい。
きちんと書かれているコードの案件に他人が入った場合、
それを汚しちゃいけない!という気持ちが働くようなのだけど、
ぐちゃぐちゃなコードの案件に途中から入る人は、
何も気にせずコードを汚し続けることになる。
これは書籍「達人プログラマー」にも書かれていることだけど、
以前ニューヨークの治安が悪かったころ、
犯罪多発地域に車をしばらく放置しても何もなかったそうだが、
その車の窓を割っておくと、またたく間に各種部品が盗まれていったらしい。
そして、その治安を向上させるために採った作戦が、
軽犯罪を徹底的に取り締まることと、地下鉄の落書きを消すことだったそうだ。
その結果、一気に凶悪犯罪の発生率が低下していったらしい。
同じことが、コードにも言えるんだよね。
未然に小さな問題を取り締まっておけば、
大きな問題が発生する可能性を一気に下げられる。
とりたてて特殊な技術があるわけではなくても、
こういうことに気を使って、モラルのあるコードを書く人。
途中から案件に入る際には、それまでの意図を理解してコードを書いてくれる人。
一緒に仕事をすると、この人と組むとやり易いな、と思うわけで、
そういう人って、実は凄い良いプログラマーじゃないかなと思うわけです。
開発をする上やサービスを作る上で一般的なセオリーと言われていること。
それは決して「一般的」ではなくて、「ある時点に当てはまる」ことな気がする。
一発で大ヒットするサービスを作れれば良いけど、
大概のサービスには原始時代から中世を経て近代へとの進化がある。
一般論におきかえて考えてみよう。
「夜に口笛を吹いてはいけない!」
という話を聞いたことはないだろうか。
これは暗闇の中で自分の位置を敵に知らせないため、という由来だと考えている。
自分の身を危険にさらさないためだ。
原始時代やまだ暗い箇所が多い中世には当てはまるかもしれないが、
今週の金曜日の夜に六本木交差点で口笛を吹くか吹かないかで、
大きな変化があることはまずないだろう。
何が言いたいかというと、
サービスを開発している上で、色々なアドバイスをいただくことがあるけど、
現在のステージでやらなければならないこと、今は意味が無いけど将来きっと役立つこと、
聞く側もきちんと理解して進めなくてはならないんだ。
例えば、いわゆるサービスの原始時代には、
進化のスピードを上げることが重要になる。
この時点では細かい所まで作り込んでいる時間もリソースも無いから、
まずはどんどん作ったものを公開してTry & Errorを繰り返していくことが必要だ。
そういう意味では、細かな箇所にかかわることに対応している余裕がない。
ただ、サービスを中世、近代へと進化させられるかどうか、
競合サービスとの差別化となるのが、最初の頃に言われていた、
細かな箇所にかかわるアドバイスだったりする。
この時期には初期のTry & Errorというスタンスから、
ある程度きちんとしたコンセプトのあるものを出す方向にシフトすべきだと思う。
初期のスピード重視から、後半はクオリティ重視になるのかな。
サービスの状況に応じて、スピードとクオリティの適切な配分をしていくことが必要だと感じてきた。
そんなことを考えていて、最近サービス開発へのアプローチを少し変えてみている。
もう一段上のレベルに持っていこうかなと。
この週末で作っているプロトタイプはちょっと面白いかも。
乗らなきゃいけない。このビッグウェーブに!
iPad発売後の状況ですが、iPad発売前からアプリが1000ダウンロードを超え、
発売前日にはAppBankへ掲載いただいたことや、
Heat On Wed.でプレゼンしたり資料を公開したせいか、
iTunesでの事前ダウンロードが進んだようで1日で500ダウンロードを突破。
発売後には、店頭デモ機にインストールしていただいていたり、
App Storeのスポットライトや注目作品に選出していただいたおかげで、
既に累積9000ダウンロードを突破しています。
この調子だとAndroid版のダウンロード数を抜くのも時間の問題です。
iPadの方が露出が多いということもありますが、
数字を見ているだけだとiPad > Androidということになりそうで、
販売台数は個人的な予想よりもかなり多いという感じです。
あと、使ってみた感想としては、個人的には大満足で、思っていたよりかなり良いです。
客観的に考えると、ノートPCを持ち歩いている営業マンなどはiPadで充分じゃないか?という感覚と、
でもやっぱり嗜好品で必需品ではないのかな?という感覚の両方が入り混じっています。
今後の展開がどうなるかは予想が難しいですが、
おかげさまで、まずは「30min.ランチマップ」は順調なスタートを切ることができました。
ダウンロードしていただいたり、RTしていただいたり、記事に書いていただいた皆さま。
本当にありがとうございます!
いやー。楽しかった!!
秘かにiPad狙ってたけど、今回は久川さんやrtiさんの方が上手でした。
自分は今できる最善のプレゼンができたと思っているので、
MHPの久川さん、それと同じくらいの喝采を浴びたrtiさんには脱帽です。
僕ももっと上手なプレゼンできるように練習しなきゃなー。
今回は自信を持ってできたし、
最近イチ押しのLibronの @jishiha さんや @yumilcy と近いくらいに
拍手をいただいたことはすごく嬉しかったです。
Libronとかなぶんのステッカーもいただきました。
FirefoxユーザーはLibronのアドオンインストールしてamazon検索してみるべきです!
検索結果に図書館が表示されるのってかなり衝撃ですよ!イチ押しのサービスです!!
コスプレ無しの @yumilcy とのプレゼン勝負は楽しいね。
衣装に頼らずとも聴衆を引きつけるプレゼンをすると思うので、
同じ土俵で勝負をすると、良い目標になるし、なんとかして勝ちたいなーと思います。
また機会があればプレゼン勝負したいですね。
MHPに選ばれた久川さんはさすがです。
アプリ購入させていただいたんですが、さすが!の出来です。
応援したくなる事業ですし、今回の勝者にふさわしかったですね。
自分のアプリのUIにはそれなりの自信はあったけど、
ああいうアプローチを観させられると、自分はまだまだだなーと感じます。
もっと発想広げなきゃね!!
あと、予想以上に面白いプレゼンばかりだった中、
自分のプレゼンをMHPに押していただいた方がいらっしゃったのは大変嬉しく思っております。
これを励みにもっと頑張ります!本当にありがとうございました。
最後に、MHPは久川さんだけど、MVPは樋上さんじゃないかな?
僕はkeynote+iphoneでプレゼンするのが初めてで、開始前に時間かかってしまったのだけど、
その間を上手くつないでいただいて助かりましたし、
一生懸命な姿から、良いイベントにしたい!という気持ちは凄く伝わりました。
「段取りが良くない」とか批判もあったみたいですが、まだ第一回ですよ?
皆で協力して第二回、第三回と良いものにして行く協力をしましょうよ!
6月30日の第二回も必ず協力させていただきます。
とにかく、すげー楽しかったですよ!
良いイベントでした。
気がつけばプログラマーとして10年戦士だ。
違う職種の人から見れば、「良いプログラマー」の定義とは、
技術力が高い人のことや、良いものを作る人、開発が早い人のことだと思う。
…でも現場はそんなに単純ではない。
システム管理や受託開発から新規サービス開発まで、
運良く色々なシチュエーションや多数のトラブルに巡り会ったおかげで、
プログラマーから見た「良いプログラマー」とはどんなものか、
自分なりの定義が出来上がってきた。
僕の中の「良いプログラマー」の定義とは、
「トラブルを未然に防げるプログラムを書く人」
だと思っている。
現場で不具合が出た時、チェック体制やテスト不足の話になるが、
それ以前に、不具合の可能性を減らすコードを書くことが大事だと思っている。
今まで10年間の中で感じてきたノウハウを残したいなと思っていたので、
少しずつブログで書いていこうと思う。
あと、僕が今まで読んだ本の中で一番影響を受けている下記の本、
プログラマーなら読んで損はないと思うのと、
この本に書かれている内容と同じテーマの話も多くなるので、
興味があれば是非読んでみて欲しいな。
