カスタムなコレクションビューセルのクラスを定義する
実は、コレクションビューのセルに任意の内容を表示したい場合、これまでのプログラムで使っているように、セルのcontentViewにaddSubviewするのは、正しい方法とは言えないのです。再利用されたセルは、サブビューとして以前のビュー(この場合はイメージビュー)を持っているので、そこにさらに新たなビューの内容が重なってしまうのです。すべてサイズと位置が同じなら、重なっても気づかないのですが、このようにサイズをダイナミックに変更すると、とたんに不具合が露呈してしまうわけです。
それでは、セルの中身を設定する正しい方法とは何でしょうか。それは、セル自体をカスタムなクラスとして定義し、そのオブジェクトを使う方法です。この場合は、セルの中身として表示するのはイメージビュー1つなので、最初からプロパティとしてイメージビューを持ったセルのクラスを定義し、そのイメージビューをあらかじめセルのビューに張り付けておきます。
このようなクラスは、UICollectionViewCellを継承するクラスとして定義できます。ここではプログラムの先頭でImageCellクラスを定義しました。
このクラスを利用するには、元のプログラムの2箇所を変更する必要があります。
まず、CollectionViewControllerのviewDidLoadメソッドの中で、再利用可能なセルとしてのIDを設定している部分です。ここは、元のUICollectionViewCell.selfの代わりに、今定義したクラスのImageCell.selfを指定します。
もう1箇所はcellForItemAtメソッドの中で、セルに画像をセットする部分です。これまでは、イメージビュー(UIImageView)のオブジェクトごと張り付けていましたが、カスタムなセルには最初からイメージビューが張り付けてあるので、その中身イメージ(UIImage)だけを設定します。また、セルの中身のイメージビューのサイズ(frame)も、毎回セルのサイズ(bounds)に合わせて設定しています。
あとは元のプログラムと同じです。動かして見ると、最初は前と同じコレクションビューが表示されます。
ただし、今度は上下に何回スクロールしても、表示が乱れることはありません。
コレクションビューの周辺のマージンとセルの間隔をカスタマイズする
これで、コレクションビューのセルのサイズを個別にダイナミックに設定することができるようになりました。ついでに、その他のカスタマイズとして、コレクションビューの表示領域の周辺のマージンと、セル同士の最小の間隔を設定してみましょう。
前者はinsetForSectionAtというメソッドで設定します。その名のとおり実はセクションごとに設定できるのですが、この例ではセクションは1つだけなので、ビュー全体のマージンと同じことです。このメソッドに対してはUIEdgeInsetsタイプの構造体によって、上、左、下、右のマージンを別々に指定できます。
後者の最小間隔は、minimumLineSpacingForSectionAtのメソッドによって指定します。これもセクションごとに設定可能ですが、ここでもセクションは1つだけです。返す値は単純なCGFloatで、この例では30ポイントを返しています。
これを実行すると、ビュー周辺からのマージンと、セルの間隔がカスタマイズされた表示となります。
コレクションビュー自体のサイズが小さいと、設定した条件を満たすことができないセルが出てきてしまいますが、その場合は空欄になります。ここに至っては、もはやセル自体の枠の大きさの確認は不要なので、グレーの背景色設定は外しました。
さらに効率的な動作を追求する
今回のプログラムを、ちょっと古めの「遅い」iPadで実行すると、コレクションビューをスクロールさせる際に動作がカクカクとぎこちなくなることがあるかもしれません。それには、セルを表示するたびに新しいイメージ(UIImage)オブジェクトを作成していることも影響しているはずです。そこで、動作を最適化するために、そもそも5種類しかない画像のイメージオブジェクトを、最初から作っておくことにしましょう。
CollectionViewControllerクラスのプロパティとして、5つのイメージオブジェクトを含むimgs配列を定義しておきます。
この変更によって、セルに画像を設定する部分のプログラムも簡略化できます。これまでは、indexPathの値に応じて、switch文を使って異なる画像ファイルからイメージオブジェクトを作成していたのに対し、こんどはindexPathの値に応じてimgs配列のインデックスを決めるだけでよくなります。
この結果の画面は前とまったく同じなので省略しますが、このような変更によってコレクションビューのスクロール動作は、スムーズなものになったはずです。
次回の予定
今回は、コレクションビューの表示、レイアウトをカスタマイズすることができました。ここまでは、単に複数の画像を並べて表示するという一方通行的な使い方でしたが、もちろんコレクションビューは双方向的な使い方もできます。つまりユーザーの操作を受け取って応答し、アクションを起こすことができるのです。次回は、そのあたりに話を進める予定です。