HSB(HSV) のすすめ
みなさんは普段アプリの開発の際にどのような形で色を指定していますか?個人的な経験と予想では
[UIColor colorWithRed:0.251 green:0.514 blue:0.663 alpha:1.000]
のような RGB での指定だったり、カテゴリやマクロで拡張して #4083A9
のような Hex 指定だったりするのではないでしょうか?このような指定になっている理由としてはデザイナーさんから指定される形式が RGB だからというのが多い気がします。デザイナー不在で開発者だけで作っている場合も RGB の方が何となくわかりやすい気がするという理由だったりします。
色の調整
デザイナーさんがいる環境であれば本職であるデザイナーさんの指示を仰ぐ形がプロダクトとしてはベストだと思うので開発者だけで色調整する場合の話です。特に色に関する知識がほぼない人です。僕自身も全く色に関する知識はありません。
RGB は Red, Green, Blue, Alpha の要素の組み合わせで色を表しています。UIColor であれば R:1, G:0, B:0, A:1 であれば赤。単純です。それではこのような真っ赤ではなく少し暗い赤が欲しい場合にはどうしますか?RGB の各値をいじくり回しますか?気がつけばとんでもない色になったりしませんか?
そこで HSB
HSB は
- Hue: 色相
- Saturation: 再度
- Brightness: 明度
で色を表現します。先ほどの例の少し暗い赤が欲しい場合は HSB 表現の赤の B の値を少し下げるだけで暗い赤になります。単純です。
こちらで配布されている日本語版 PDF の P18 でも進められているように個人的にも HSB は非常に色の調整がやりやすいです。
ちなみにこの PDF はおすすめなので1度見ることをおすすめします。
デモアプリ等で適当な色が欲しい時
ランダムな色が欲しい場合は HSB のうち H の値を変えるだけでそれなりに色が変わります。特に連続した色が欲しい場合は H の値を順番にループさせてやれば非常にカラフルで小綺麗な色になります。
補助ツール
任意の色が欲しいとかはさすがに
[UIColor colorWithHue:0.560 saturation:0.621 brightness:0.663 alpha:1.000]
とかいきなり書いたりはできないですよね。色を見ながら作りたいのでアプリで補助します。個人的におすすめは
上記2つの合わせ技です。
HexColor は OS の Color Picker を単独のアプリとして動かせるアプリです。このアプリ自体にも色を Hex や NSColor に変換してPasteboardにはき出すことができますが、Developer Color Picker プラグインを入れることで UIColor にも吐き出せるようになります。これらのアプリとプラグインは iOS 開発以外でも便利なのでおすすめです。他にも sip なんかもあります。対応するフォーマットが多いのが特徴です。
まとめ
HSB は同系色の調整が割と直感的にできるのでおすすめです。HSB だけでいろんな色を作るのは難しいかもしれませんが RGB でざっくり色を作ってから HSB で微調整するとか、スポイトツール的な機能で欲しい色を抜いてきて HSB で調整とかすると色の幅が広がっていいのではないでしょうか。
Ono '斧' を触ってみた
先日、AFNetworking や NSHipster で有名な Mattt が Ruby の Nokogiri 風の XML & HTML パーサー Ono 斧 を公開しています。
早速少しだけ触ってみた
CSS Selector でのパースをサポート
XPath での指定なら今もいくつかライブラリがありますが CSS Selector をサポートしたものは私は知りませんでした。個人的には Nokogiri を連想させる最大のポイントです。
Subscripting 対応
エレメントのアトリビュートへのアクセスは Subscripting 対応になっています。
element[@"href"];
という書き方で href アトリビュートの値が取得出来ます。
NSDate のサポート
XML が対象になるかと思いますが dateValue メソッドで NSDate 型を返してくれます。 "yyyy-MM-dd'T'HH:mm:ssZ" のみのサポートですがお手軽ですね。
Block を使った列挙が可能
- (void)enumerateElementsWithXPath:(NSString *)XPath block:(void (^)(ONOXMLElement *element))block; - (void)enumerateElementsWithCSS:(NSString *)CSS block:(void (^)(ONOXMLElement *element))block;
このような列挙もサポートされています。また NSFastEnumeration に対応させた実装のサンプルにもなっているので勉強になります。
シンプルな実装
ファイルとしては実質1組の .h .m ファイルでクラスの数はそれ以上ありますが規模の小さいコードなので先に挙げたように Subscripting サポートや NSFastEnumeration 対応などなど実装の参考になりそうなちょうど良いサンプルです。
まだまだこれから
できたばかりということもあるのか CSS での id 指定が以下のようなエラーを吐いてしまってだめでした。
XPath error : Invalid expression
//[@id = 'header-container']
Nokogiri と同じ指定をしても他にもページによってはエレメントが見つからないこともありました。Mattt は Nokogiri と同等のものを目指しているのか名称だけリスペクトしてるのかわかりませんが個人的には Nokogiri と同等の動きをするライブラリになってくれることを期待しています。
今週末に CocoaPods 対応やドキュメント等が揃ってくるようなのでこれからも楽しみにしたいと思います。
簡単な動作確認ができるサンプルを置いておくので気になる方は試してみてください。
App Switcher に表示される View を差し替える補助ライブラリ MMAppSwitcher
以前アプリの画面を開いているアプリケーションのプレビュー画面から隠すというものを書きました。先日たまたま見つけたライブラリがこのような処理を補助するものがあったので一応ご紹介。
MMAppSwitcher というライブラリです。使い方的には MMAppSwitcherDataSource プロトコルを実装して App Switcher に表示して欲しい View を返す感じです。
ライブラリの内部的には UIApplicationWillBeginSuspendAnimationNotification
という非公開な Notification をきっかけに App Switcher 用の View を表示させているようです。非公開なものなので若干審査だったり将来のバージョンアップとか大丈夫かな?って気がしますね。
こんなことをしている理由として推測するには、applicationDidEnterBackground:
のタイミングだと
- アプリ最前面
- ホームボタン1回押し
- ホーム画面が表示されたと同時にホームボタン2回押し
というような操作をされた場合には applicationDidEnterBackground:
がまだ呼ばれていないっぽいので App Switcher 用の View を用意できないんですね。だからこのライブラリの作者は willEnterBackground 相当のタイミングを探った結果 UIApplicationWillBeginSuspendAnimationNotification
にたどり着いたのだと思います。
僕がやったような "ほぼ期待する動作をする" アプローチをとるのか、MMAppSwitcher
のように攻めるのかは自己責任で。
どうでもいいけど
この「ホームボタン2回押し」で表示される機能ないし画面の Apple 的正式名称ってなんなんでしょうね?個人的に AppSwitcher って気持ち悪い。
iOS アプリ開発に関わる人にぜひ読んで欲しい本[の宣伝]
僕は iOS アプリのコーディングをやっていて主にそっち方面のブログエントリを書いていますが今日は本の紹介というかタイトル通り宣伝です。
アプリ開発をやってる僕ですが結構 UI / UX は気になるタイプで仕事中もデザイナーにいちゃもんつける面倒くさいやつなんですが今回同僚が本を書きました。
iOS 7デザインスタンダード 最新のフラットデザインに対応-iPhoneに最適なUI・UXを徹底的に解説!
- 作者: 荻野博章,丸山弘詩
- 出版社/メーカー: インプレスジャパン
- 発売日: 2013/11/22
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る
この本は主にデザイナー向けに書かれた本です。特に今まで UI / UX デザインを手がけた経験があまりない方や別プラットフォームがメインだったデザイナー向けです。僕の思ったターゲット読者別のおすすめポイントを簡単に紹介します。
デザイナー向け
先に挙げたように iOS アプリの UI / UX をバリバリやっている人向けというよりはこれから学ぶ人に最適だと思います。別プラットフォームが主戦場だった人にはチャプター 02 の iOS 標準 UI の紹介とその使いどころの説明で基礎的な知識を学べます。チャプター 03 はこの本の特徴だと思いますが実際に筆者がアプリの企画/デザインをする際にどのようなことを行っているのかを垣間見ることができる実践的な話が詰め込まれています。業務として UI / UX デザインの経験が浅い方はもちろん経験豊富な方も他のデザイナーがどのような作業フローをやっているのか?どのような視点で考えているのか?という事を知る機会は限られていると思うので1度読んでみると視野が広がるかもしれません。
開発者向け
iOS 6 から iOS 7 への標準 UI の変更点なども記載されていて iOS 7 向けのアプリ開発になれていない開発者の人でもその差異を知るのに役立ちます。巻末には各種画像のサイズや UI パーツの標準的なサイズがまとまっているのでちょっとリファレンス的に読むのもいいかもしれません。デザイナーがどんなことを考えてどのような視点でアプリのデザインを行っているか?ということは開発だけを行っていると知る機会の少ないのでこの機会に知ってみるのもいいと思います。
企画側・開発依頼側向け
デザイナーでも開発者でもないアプリの企画側の方や主にアプリ開発を依頼する側(ex: 既存 Web サービスのアプリ化を考えているような方)にも最適だと思います。デザイナーがどのような視点でアプリを良い物にするために考えているかを知ることができます。モバイルアプリの特性も知らずに考えているとあれもこれも載せたくなることは往々にしてありますが、この本を読めば本当にモバイルアプリとして iOS アプリとして必要な要素とは何なのか?ということに気づけると思います。
最後に
iOS アプリ開発に関わる人全てに1度読んでもらいたい本です。とても読みやすくすいすい読み進められる本なので是非。
iOS 7デザインスタンダード 最新のフラットデザインに対応-iPhoneに最適なUI・UXを徹底的に解説!
- 作者: 荻野博章,丸山弘詩
- 出版社/メーカー: インプレスジャパン
- 発売日: 2013/11/22
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る
おまけ: 別の同僚も iOS のデザインについて本を書いたのでそっちも読まないと。iOSフラットデザインの作法
dispatch_source の DISPATCH_SOURCE_TYPE_TIMER で timeout 処理を実装する
先日、x秒たったらある処理をキャンセルするといういわゆるタイムアウト処理を実装する必要があったときに dispatch_source を使ってハマったので備忘録。
当時ググっても繰り返し一定間隔で処理を動かすサンプルはすぐ見つかったのでそれをベースやっても期待する動きならなかった。結局は「エキスパート Objective-C プログラミング」にサンプルがのってて助かりましたという話。
Xcode のスニペット形式だとこんな感じ
/// dispatch_source を生成。timer を回す queue を指定。 dispatch_source_t timerSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, <#dispatch_queue#>); /// timeout の時間を "seconds" プレースホルダーに "15" のように入力。"leeway_seconds" にはズレの許容範囲を入力 dispatch_source_set_timer(timerSource, dispatch_time(DISPATCH_TIME_NOW, <#seconds#>ull * NSEC_PER_SEC), DISPATCH_TIME_FOREVER, <#leeway seconds#>ull * NSEC_PER_SEC); /// timer 発火時の挙動を実装 dispatch_source_set_event_handler(timerSource, ^{ <#code#> /// 後始末 dispatch_source_cancel(timerSource); /* もしくは #if !OS_OBJECT_USE_OBJC dispatch_release(timerSource); #endif */ }); /// timer キャンセル時の挙動を実装 dispatch_source_set_cancel_handler(timerSource, ^{ <#something_clean_up#> #if !OS_OBJECT_USE_OBJC dispatch_release(timerSource); #endif }); /// timer の動作開始 dispatch_resume(timerSource);
素直に NSTimer を使えばいいじゃん?というのがあったんだけど、その処理全体がバックグランドディスパッチキュー上で実行されてたので Runloop を自分で回すのは嫌だったり、メインスレッドにタイマーをセットするとスレッドをまたぐ事になるのでシビアなタイミング(実行順序)を求められてたので dispatch_source を使いたかったというわけです。
ホントに「エキスパート Objective-C プログラミング」はすばらしい。
UICollectionView で UITableView のセクションヘッダー風の SupplementaryView を実装する
UICollectionView は昔なら UITableView を使って頑張って実装していようなグリッドレイアウトな UI を UITableView ライクな I/F で実装できる素敵なやつです。UITableView ライクな I/F とは言いましたが実は細かい挙動が UITableView とは違っています。UICollectionView で UITableView のセクションヘッダーのようなものを実装するには SupplementaryView を使います。でも普通に UICollectionViewFlowLayout を使っても SupplementaryView はスクロールすると Cell と同じようにそのまま通り過ぎてしまいます。UITableView のセクションヘッダーみたいに同一セクション内の場合に上端に居続けてくれません。UITableView のセクションヘッダーみたいなフローティングした(上端に張り付いたような)セクションヘッダーを実装したい。じゃあ、似たような挙動になるようにしようっていうのが今回の話です。
とりあえずググる
特にライブラリとしてまとまっているものが見つけられませんでした。(あとで調べたら vast-eng/uicollectionview-gridlayoutというのがありました。)しかし、stackoverflow で同じようなことをしたいという質問が見つかりそこから gist に上げられたコードを見つけました。
Sticky Headers at the top of a UICollectionView
やや難あり
さっきの gist ですが、確かに UITableView のセクションヘッダーみたいに同一セクション内の場合に上端に居続けてくれるのですが、全体的に位置が下がり気味。何かが違う。
どノーマルの UICollectionViewFlowLayout を使ったものがこちら
gist のコードをそのまま適用したのがこちら
修正
ずれる理由は UICollectionViewFlowLayout
の sectionInset
の値が考慮されていませんでした。修正後のコードはこちら。
UIEdgeInsets sectionInset = self.sectionInset;
origin.y = MIN(
MAX(
contentOffset.y + cv.contentInset.top,
(CGRectGetMinY(firstObjectAttrs.frame) - topHeaderHeight - sectionInset.top)
),
(CGRectGetMaxY(lastObjectAttrs.frame) - bottomHeaderHeight + sectionInset.bottom)
);
公開
修正したコードを UICollectionViewFlowLayout
のサブクラスとして github にあげています。もし使えそうなら使って見てください。
CSNFloatingHeaderViewFlowLayout
CocoaPods でインストールできます。
pod 'CSNFloatingHeaderViewFlowLayout', '~> 0.0'
Thank you toblerpwn.
はてなインターンのサンプルコードを読んでの感想
はてなさんが
はてなインターンで利用したiOSアプリ等のサンプルコードを公開しました - Hatena Developer Blog
という素敵なサンプルコードを公開してくれたので、好き勝手に感想を書いてみたいと思う。本来は Web アプリ側も動かしたかったんだけど、ウマく環境が作れなかったのでアプリはコードを読んだだけで動かしてはないです。あと、細かい検証とかしてないので勘違いとかしていると思うので指摘してください。
参考になった真似したい
NSError と UIAlertView の使い方
NSError
の内容ベースでエラーアラートを出すのは理にかなっていると思った。OS 側が返す NSError
は期待する値が入っていない不親切な場合があるので、オリジナルの NSError
から一度適切な値を持った NSError
を再生成して返すクラスを介したらいいのかもしれない。
すっきりした UIViewController
iOS アプリを作り慣れていない人は特に直接ネットワーク処理を書いてしまったりモデルの管理コードを書いてしまいがちで UIViewController
の肥大化するのがありがちなパターンだと思う。しかし、このサンプルはネットワーク処理、モデル管理等を綺麗に分けているのは見習うべきだと思った。
通信とモデルの管理の関係
普段僕は Web API を叩くクラスとそれを元にモデル生成するのを同じクラスに書いていたけど、このコードのようにネットワークはシンプルに Web API を適切に触ることだけにし、パースやモデルの生成、管理はモデル管理クラスに分けた方が綺麗だし、のちのメンテナンス性や拡張性が確保されていいと勉強になった。
isEqual:
と hash
をちゃんと実装
結構面倒で実装してないときがあるんだけど、モデルクラスがちゃんと isEqual と hash メソッドをオーバーライドしているのが素晴らしい。特に hash
メソッドを実装するのを忘れていると NSSet
に突っ込んだ時の振る舞いが期待通りにならずに軽くハマるので注意。
KVO をウマく使っている
KVO を使って ViewController がモデルを見ているあたりは Mac アプリ実装経験者っぽくてなんか素敵。NSError
の localizedFailureReason
とか使っている点も同様。KVO を使って、TableView の更新をかけているのは凄く勉強になる。mutableArrayValueForKey:
は知らなかった。勉強になった。
NSIndexSet
の使い方
indexSet をうまく使っているのは素敵だと思った。 mattt が WWDC でも言っていたので使う機会があったら自分も使いたいと思ってる。
気になったところ
UIAlertView+NSError
self.message = [[NSArray arrayWithObjects:[error localizedFailureReason], [error localizedRecoverySuggestion], nil] componentsJoinedByString:@"\n"];
一瞬、[error localizedFailureReason]
が nil
だったら落ちるんじゃないかと思ったけど、よく考えると arrayWithObjects:
は nil
終端として動作するので落ちない。[error localizedFailureReason]
、[error localizedRecoverySuggestion]
が nil
なら self.message
はカラのままで済むのでスマート。ただ実際にあるかどうかはわからないが、逆にいうと [error localizedRecoverySuggestion]
が入っていて[error localizedFailureReason]
が nil
を返す場合には、先に渡されている nil
の評価がされてしまって仮に値が入っていても [error localizedRecoverySuggestion]
は self.message
は nil
となる。この挙動を忘れているとある日ハマる。もっとマジメに実装するなら NSMutableArray
に nil
チェックをしながら足していく感じになるのかもしれない。
NSDictionary に入れる値の nil チェック
IBKMInternBookmarkAPIClient が引数の nil
チェックもせずに NSDictionary
に突っ込んでいるのは落ちる可能性があるので危険。使う側が意識すれば落ちないだろうけど、渡されたデータの妥当性チェックはデータをもらう側に責任があると思うのでチェックした方がいいと思う。
addObserver:~ したら remove~ する
KVO の監視開始、NSNotificationCenter への登録をしても、それらの解除を書いていないのは良くない。
Be sure to invoke removeObserver: or removeObserver:name:object: before notificationObserver or any object specified in addObserver:selector:name:object: is deallocated.
と NSNotificationCenter
の addObserver:selector:name:object:
にも書いてある。
NSNotificationCenter
の remove は複数回読んでも害はないけど、KVO の remove は必ず add を一対でないと行けない(add していない状態で remove すると落ちる)のでライフサイクル的に一対になるようなところに入れるしないといけないので注意が必要。
24時間表示を Off でもちゃんと機能するように NSDateFormatter を使う
NSDateFormatter
に locale を "en-US" でセットしていないけど、24時間表示を Off にしている端末でも期待する動作をするのか不安。timeZone を指定していると大丈夫なんだろうか?未検証。
まとめ
気になる点はあるけど、これだけ綺麗にまとまって、なおかつそれなりに実戦経験がある人でもやってなさそうだったり知らなそうなことをサラッと見せてくれているので、このコードで Objective-C / Cocoa を勉強するインターン生は幸せもんだなぁと思った。僕も来年インターンで入れないかな?