月別アーカイブ: 2013年7月

iPhoneのステータスバーの高さを取得する【iOS6】

【追記】
このエントリだいぶ古いです。iOS6の時のオハナシ。iOS7以降は挙動変わります(‘A`)
———————————–

IMG_2027
ステータスバーのスタイルをBlackTranslucentにするとViewが画面いっぱいに広がるが、ステータスバーを避けて表示しなければならないものもある。例えば上の画像の左上の電子国土マークがそれ。

ここで、ステータスバーの高さは20ピクセル! と決め打ちで上20ピクセル分を固定値で避けると、インターネット共有中などの時に下のように死亡する。
IMG_2028
で、ステータスバーの高さを正しく取得するには、UIApplicationのstatusBarFrameプロパティをみる。
[UIApplication sharedApplication].statusBarFrame
こいつが {{0, 0}, {320, 20}} とかのCGRectを返してくるのでそのサイズで判定。
iPhoneで縦位置固定の場合は height だけ見ればいいんだけど、この値は画面の回転が反映されないWindow座標系で得られるので回転する場合は向きを考慮する必要がある。
でもUIApplicationのstatusBarOrientationをみたりとか座標を変換したりとかは面倒いので、欲しいのはステータスバーの高さだけなので、幅よりも高さが大きくなることはないと決めつけてheightかwidthのどちらか小さい方をステータスバーの高さとする、ってのが手っ取り早いと思う。(ウンコ判定だけれども、この判定にする理由は下の方にもある)

これで一件落着。

と、思うのはちょっとだけ早い。

テザリングするとステータスバーの高さが変わる。自分のアプリがフォアグランドになってる最中でも、いきなり接続されたり解除されたりして、思わぬタイミングでステータスバーの高さが変わる場合がある。そんな時でもステータスバーの高さが変わったタイミングでそれに合わせた画面表示に変更したい。

どうするか。

そんな時のためのNotificationが用意されているので、Observeすればよい。

こんなカンジ。
UIApplicationWillChangeStatusBarFrameNotificationはステータスバーの高さが変わる直前に通達されて、userInfoで変更後のステータスバーのサイズを得られる。UIApplicationDidChangeStatusBarFrameNotificationはステータスバーの高さが変わった後に通達されて、userInfoで変更前のステータスバーのサイズを得られる。詳しくはUIApplicationのReferenceのNotificationsのあたりを参照のこと。

この2つのNotificationは「ステータスバーのフレームが変化した時」に通達されるので、ステータスバーが太った時だけでなく、画面が回転した時にも通知がやってくる。
この時 WillChange の方を監視する場合は注意が必要で、userInfoには回転後のステータスバーのWindow座標が入っているけど、この通知が来る時点ではまだ回転が始まってないからUIApplicationのstatusBarOrientationなどは回転前の値を返すのでその向きの判定には使えないのだ。
なので、ステータスバーの高さだけ知りたいのならUserInfoで得られる変換後のフレームのheightかwidthか小さい方でいいやみたいな。

このふたつのNotificationはアプリがフォアグランドの時にステータスバーのサイズが変わった場合のみ通達される。バックグランドでステータスバーのサイズが変わった時(例えば設定アプリでインターネット共有を切り、ステータスバーの高さが通常に戻ってからアプリがフォアグランドになった時)にはこの通達はこない。

以上、EnterForegroundだかBecomeActiveだかのタイミングでUIApplicationのstatusBarFrameを確認し、上記いずれか(または両方)のNotificationでフォアグランド時の高さの変更に備える、ってやるとステータスバーの高さを正しく扱えるようになる。

ハズ。

std::array+初期化リストの不可解な挙動

std::arrayを構造体の初期化リストで初期化しようとすると挙動が不可解なのでメモ。
(環境はXCode4.5)

// 0
まず単純なintのarrayだと、波括弧ひとつで期待した通りに書ける

// 1
ところが構造体の初期化リストの入った配列の初期化リストを渡すと、XCode4.5の環境では「要素数過多」とかいう意味不明なエラーが出る。

// 2
ググったところ、波括弧をひとつ余計につけるとエラーが出なくなるっていう情報があったので試したら本当に大丈夫だった。( std::arrayは構造体の初期化が無理なのかと思ったらいけた
この余分な波括弧は何を表してるんだろう。。。? 試しにさらにもうひとつ波括弧を増やしたらエラーが出た。やみくもに増やせばいいってもんでもないらしい。

// 3
いろいろ試してみたが、どうやらエラーになるのは「配列の第一要素が波括弧で始まるかどうか」らしい。続く要素は関係ない。第一要素は波括弧で始まりさえしなければok。

以上から、std::arrayを初期化リストで初期化する場合に第一要素が ‘{‘ で始まるのはエラー で、これを回避するには、

・全体に波括弧をひとつ余分にかぶせる
・第一要素が波括弧で始まらないようにする(明示的なコンストラクタ呼び出しにするとか、波括弧の要らない単一要素での初期化にするとか)

みたいな対策が必要そう。。。。

これが納得いかないのは、std::vectorはこんなことなくてフツーに

って書けるのですよ。

あー、なんか納得がいかないっ!!!!!!

Lambda/Blocks の引数内のObjective-C++クラス派生関係

オブジェクトを引数に取るLamdaやBlockの変数に対して、そのオブジェクトと派生関係にあるクラスのオブジェクトを渡すとどうなるか試してみた。
環境はXCode4.5。

まず、LambdaでC++ classの場合。

一つ目は A0 に A0 を渡してるから当然OK。
二つ目はまったく関係のないクラスのオブジェクトを渡してるから当然エラー。
三つ目はA0を扱う関数変数にその派生クラスを扱う関数を渡している。これはアップキャストにあたるので当然エラー。
四つ目はA1を扱う関数変数にその基底クラスを扱う関数を渡している。これはダウンキャストにあたるので当然OK。
という、至極当たり前な結果になった。

同じ事を Blocks と Objective-C classでやっても同じ結果になる。

さて、ここからが本題。
じゃあ、LambdaでObjective-Cクラスを扱ったらどうなるの? BlocksでC++クラスを扱ったらどうなるの?
(クラス関係は上に出てきたまま)

LambdaでObjective-Cクラスを扱った場合、何故かアップキャストになる代入に対してもエラーが発生しない。ただしクラス関係が全く関連がない場合はきちんとエラーになるのでこの挙動はようわからん。。。
そしてBlocksでC++クラスを扱った場合、ダウンキャストになる代入に対してもエラーになる。。。 これは微妙に困る。
いやLambda使えよ、って言われてもGCDやる時はそうはいかないじゃないですか。

この辺の挙動将来はちゃんとしてくれるだろうか。