UICollectionViewCellのサブクラスをiOS 5で共存させる | HMDT Blog

UICollectionViewCellのサブクラスをiOS 5で共存させる


iOS 6ではUICollectionViewってのが追加された。テーブルビューを超えるとっても便利なクラス。でも、これを使ったアプリをiOS 5でも動作させようとしたら、ちょっとした問題が。

新機能を使ったアプリを過去のバージョンのOSで動作させる場合、いくつかのテクニックがある。新規フレームワークはweakリンクするとか。新規クラスは直接記述せずNSClassFromStringで取得するとか。そうやって一個ずつクリアしていったんだけど、これどうしよう?っていう問題にぶつかった。

UICollectionViewでは、UICollectionViewCellっていうセルクラスを使う。画面に表示する内容をカスタマイズするもので、テーブルビューのセルみたいなやつだ。UICollectionViewCellのサブクラスを作るってのが、普通の使い方になる。

これが問題に。サブクラスを作るということは、ビルド時に親クラスを指定してやらないといけない。指定された親クラスは、バイナリファイルに解決が必要なクラスとして記述されて、起動時にdyldが解決しようとする。このとき、UICollectionViewCellクラスが存在しないと、起動に失敗する。従って、バイナリにUICollectionViewのサブクラスが含まれていると、そのクラスを実行時にインスタンス化するしないに関わらず、iOS 5での起動ができない。

うーむ、困った。どうしよう?やっぱりUICollectionViewを使うのをやめるとか、iOS 5は対応しないとか、後ろ向きな解決策も考えた。または、iOS 5でも動作する互換クラスを使うとか(PSTCollectionViewみたいな)。けど、釈然としない。おれは、iOS 6ではUICollectionViewを使って、iOS 5では使わないっていう、そういうバイナリを作りたいんだよ。

いろいろ悩みまくったあげく、トリッキーな解決策にたどり着いた。要は、UICollectionViewCellのサブクラスが起動時に存在するから悪いのだ。ならば、始めはUIViewのサブクラスにでもしておけばいい。そして、起動した後で、その親クラスを差し替えればいいのだ。

「親クラスを差し替える?」なんだそりゃ、と思われたそこのあなた。Objective-Cではそんなことだってできてしまいます。だって、変態言語だもの。

Objective-CのランタイムAPIには、class_setSuperclassというのがある。文字通り、親クラスを設定するためのAPIだ。これを使えば、あるクラスの親クラスを動的に変更することができる。もちろん、下手にこんなことをすればとんでもないことになるので、使用には細心の注意が必要だ。ドキュメントには一言、「You should not use this function.」とだけ書いてある。そんなら、こんなAPI用意しておくなよ。

実験してみたloadメソッドに次のように書く。

+ (void)load
{
    // UICollectionViewCellのクラスを取得
    Class   collectionViewCellClass;
    collectionViewCellClass = NSClassFromString(@"UICollectionViewCell");

    // 存在する場合
    if (collectionViewCellClass) {
        // 親クラスを差し替える
        Class   magazineCollectionCellClass;
        magazineCollectionCellClass = NSClassFromString(@"HJMagazineCollectionCell");
        class_setSuperclass(magazineCollectionCellClass, collectionViewCellClass);
    }
}

これを使えば意図通りの結果を得ることができた。とっても気持ち悪いが、とりあえずこれでしのげるかどうか、しばらく試してみる。しかし、Objective-Cフリーダムすぎる。

  1. コメントはまだありません。

  1. トラックバックはまだありません。