Code for History

"Code for History"はIT技術を歴史学上の問題の解決に使うコミュニティです。強調したいのは、我々にとってIT技術は「手段」であって「目的」ではありません。「目的」は歴史学上の問題を解決する事であって、必要であればITでない手段も活用します。常に最優先なのは、問題を解決することです。

iOS5/Xcode4.2での挙動異常/バグいくつか(11/16さらに追記あり)

iOS5/Xcode4.2本格的に使い始めたけど、本当にバギーな感じ。
仕様変更とかのレベルじゃなくて、納得のいかない前バージョンと異なる挙動が多過ぎる。

挙げていけばキリがないとは思うけど、情報共有のためにいくつか挙げてみる。
いずれも実機だけでなく、シミュレータでも再現する。

UITableViewでsetFrame後の再レイアウト処理がおかしい


iOS4.3以前だと、デバイスの方向変換等でUITableViewのFrameを変更しても、自動で正しいレイアウトになっていた*1

ところが、iOS5だとご覧の通り。

setFrameをした後のセルの配置が、全部一番上に集まってわけの判らない状態に。
一度各セルの「あるはずの」場所を画面外に出して再描画してやるか、それともsetFrame後にreloadDataを走らせて全部描画し直さない限り正しく描画されない。
ただ、これiOS5だと必ず起こるわけでもなく、Xcode4.1以前のコンパイラコンパイルしたコードだと、iOS5でもきちんと再描画されるっぽい。
Xcode4.2以降のコンパイルで、iOS5での実行時のみ、描画がおかしくなる感じ。
なので、OSだけじゃなくコンパイラも絡んだ問題*2なのかも。

MKMapViewにsetFrameかけると中心点がズレていき、centerCoordinateを保存しておいても直せないMKMapViewがsetFrameで正しいサイズにならない

(2011/11/8追記)本件、その後の調査で原因が別だと判りました。

MKMapViewにバグがあるのは確かですが、それは中心点がズレる事ではなくて、setFrameを行った場合に正しいサイズに描画されない場合がある事によるものでした。
具体的な現象としては以下の画像の通り。

上記の通り、setFrameしても設定したのと違うサイズに描画されるために、中心点がズレて見えていたのが原因の模様。
確かによく見ると、元画像も端が黒く見えてるわ。

で、何とか回避する方法はないかと思っていろいろ試してみたところ、MKMapViewの描画範囲に地図の端点(南北極や、経度180°付近)が含まれると、そのタイミングで正しいフレームサイズに修正される事を発見。

よってwork aroundとしては、次のような感じにすればこのバグは回避出来そう。

CLLocationCoordinate2D center = mapView.centerCoordinate;
[UIView animateWithDuration:0.0 animations:^{
    mapView.frame = newFrame;
    [mapView setCenterCoordinate:CLLocationCoordinate2DMake(86.00,179.99) 
        animated:NO];
    [mapView setCenterCoordinate:center animated:NO];
}];
11/15 さらに追記:

上記のwork aroundは問題ありです。
setFrameの問題に関しては修正されるのですが、上記を行った時点でどうも内部でのMKAnnotationとMKAnnotationViewの対応付けが破壊されるようで、以下のような現象が発生します。

ユーザの選択したAnnotationViewと、Select状態になるAnnotationViewの対応が異常になるという現象が発生します。
何度も試しましたが、上記の

[mapView setCenterCoordinate:CLLocationCoordinate2DMake(86.00,179.99) 
        animated:NO];

コメントアウトすると直りますので、このwork aroundは使えなさそうです。

代替としては、こんな感じでうちでは動いています。

CLLocationCoordinate2D center = mapView.centerCoordinate;
[UIView animateWithDuration:0.0 animations:^{
    mapView.frame = CGRectMake(newFrame.origin.x,newFrame.origin.y,newFrame.size.width-1,newFrame.size.height-1);
    mapView.frame = newFrame;
    [mapView setCenterCoordinate:center animated:NO];
}];

どうも、1回のsetFrameだとうまくいかなくても、何度か繰り返してるとうまく行くよう。
とはいえ、同じ値をセットすると内部キャッシュされてるようで処理がスルーされるので、少し違う値をセットしてみると、うまく動きました。
今のところ、MKAnnotationとMKAnnotationViewの対応付けが破壊されている様子もありません。

また、setCenterCoordinateでMKAnnotationとMKAnnotationViewの対応付けが破壊される原因ですが、大きな経緯度を与えたためではなく、どうもsetCenterCoordinateを連続して呼ぶと破壊されるようです。
setCenterCoordinateで与える値が(86.00,179.99)のような値ではなく(0.00,0.00)であっても破壊されるのを確認しています。

以上、ここまで追記。
以下は11/05段階の元の記事:
=====================================
1つ目の問題は、挙動が違うといってもreloadDataをかけてやれば直るので大した事ではないのだけど、こっちは直す方法が今のところ見つかってなくて厄介…。
こっちは地図アプリ作ってるので、この辺が制御出来ないと死活問題なので本当に何とかして欲しい。

iOS4.3以前だと、デバイスの回転等でMKMapViewにsetFrameかけてやっても、中心点が保存されていた。
こんな感じ。


MapKitは好きなところ*3もあるし嫌いなところ*4もあるプロダクトだが、フレームを変更しても中心経緯度が維持されるのは非常にいい特徴だった。
一応、上記キャプチャの処理ではsetFrame直前のcenterCoordinateを保存し、setFrame後に戻す、という事も飽くまで念の為に行っているのだが、そんな事をしなくともきっちり元の場所が維持される。

が、iOS5以降だとこう。


setFrameを繰り返すと、どんどん中心点がズレていってしまう。
しかもこれ、念の為の『setFrame直前のcenterCoordinateを保存し、setFrame後に戻す』という処理を入れていても直らないので、今のところ回避しようがない。
うちのアプリは地図の制御がキモのアプリなので、こんな下手打たれると非常に厳しい。

エラーが発生した場合、デバッガで詳細が判らない

全部ではないのだけど、一部のエラーでiOS5以降だとデバッガでエラーの詳細が判らなくなる。
たとえば、こんなエラーを仕込んでみる。

[[[NSMutableDictionary alloc] init] setObject:nil forKey:nil];

iOS4.3以前をデバッガにアタッチすると、出てくるエラーはこんな感じ。

エラーの種類だけじゃなく、どこのクラスの何と言うメソッドの中で発生したかまでしっかり追跡出来ます。
が、iOS5以降だと、

発生したエラーの種類は判るけれど、どこで発生したのかさっぱり判りません。


とまあ、割と一事が万事こんな感じで、それまで普通に動いていた機能が動かなかったり、おかしな挙動したり、iOS5になって細かなUIデザインが一新された!とか喜んでいる人もいるけど、開発者視点から見るとすごく退化してる、というかバグ多過ぎという感じがする。
iCloud絡みでも、大した利点ない割に、勝手に始めた事に対して「こういう作り方は許されなくなった」とか、こちらはこちらなりにユーザ利便性を考慮して選択している内部構造に対して納得のいかない修正を要求されたりするし、iOS5はすごく開発者泣かせだなあという印象を、今のところ受けてます。

*1:正確には、セル毎にdrawRectが走るので、それに合わせてセルの中身だけ再配置してやれば、正しいレイアウトになっていた。

*2:Xcodeコンパイラは、4.1までLLVM GCCだったのが、4.2からLLVMにデフォルトが変更。

*3:タッチ操作による時のみとはいえ、連続ズームを実現しているところ等

*4:タッチ操作時以外は離散ズームになってしまうところ、東西経度180度/南北緯度85度がビューの端で止まってしまうところ等

© Code for History