【Swift】UICollectionViewLayoutの使い方。コレクションビューのセルの配置をカスタマイズする。(Swift 2.1、XCode 7.2)
UICollectionViewLayout
前回までの記事で、UICollectionView(以下、コレクションビュー)を使ってデータを格子状に並べる方法について説明した。⇒「記事」
前回の記事では、単純にセルを碁盤の目のように並べて表示したが、もっと個性のある並べ方にしたいときがある。そんなときに利用されるのがUICollectionViewLayout(以下、コレクションビューレイアウト)である。
コレクションビューレイアウトとは、コレクションビューに表示するセルのレイアウトを自由にカスタマイズするために利用されるクラスである。
コレクションビューレイアウトを継承したクラスを作成し、メソッドをオーバーライドして自作レイアウトを実装する。
自作レイアウトのクラスを作る。
では、実際に自作レイアウトを実装してみよう。
なお、以降の説明は「Collection Viewの使い方」で行った手順の続きからとなる。自作レイアウトにはセクションも含めることができるが、コードを把握しやすいようにするため今回はセクションの実装は行わない。
作り始める前にどのようなレイアウトにするかを考える。今回は下図のようにコレクションビューの幅の半分が1つのセルの幅になるようにし、セルがY軸方向に向かってジグザグに配置されるレイアウトを作ってみよう。
メニューから「File」⇒「New」⇒「File…」を選択する。
テンプレートを選択する画面が表示されるので、「iOSのSource」⇒「Cocoa Touch Class」を選択する。
クラス名を入力する画面が表示されるので、Classに「TestCollectionViewLayout」、Subclass ofに「UICollectionViewLayout」を入力し、Nextボタンを押す。
保存先を指定する画面が表示されるので、プロジェクトと同じ場所であることを確認し、Createボタンを押す。
次に、Main.storyboardを開き、コレクションビュー(下図赤枠)、アイデンティティインスペクタボタン(下図紫枠)の順にクリックして設定画面を表示する。設定項目のLayoutに「Custom」、Classに「TestCollectionViewLayout」を入力する。
これでコレクションビューに自作レイアウトが使われるようになった。
自作レイアウトのコードを実装する
ViewController.swiftを開き、以下のコードに変更する。
オーバーライドが必要なメソッドは「レイアウトを準備するメソッド」、「レイアウトを返すメソッド」、「全体サイズを返すメソッド」の3つである。
「レイアウトを準備するメソッド」は、コレクションビューが描画される前に1回だけ呼ばれるメソッドである。コレクションビューに含まれるセルのX座標、Y座標、幅、高さを保持するUICollectionViewLayoutAttributeクラスのインスタンスを作り、インデックス番号順に並べて配列(以下、レイアウト配列)に格納しておく。
「レイアウトを返すメソッド」は、コレクションビューに表示するレイアウトの配列を返すメソッド。引数rectにコレクションビューのサイズが渡ってくるが、今回は考慮不要なので、「レイアウトを準備するメソッド」で作ったレイアウト配列をそのまま返すようにした。
「全体サイズを返すメソッド」は、レイアウトに従ってセルを配置したあとのレイアウトの全体サイズを返すメソッド。この値が小さいとコレクションビューをスクロールしてすべてセルを見れなくなる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
// // TestCollectionViewLayout.swift // import UIKit class TestCollectionViewLayout: UICollectionViewLayout { let numberColumns = 2 //列数 let height:CGFloat = 50 //セルの高さ //レイアウト配列 private var layoutData = [UICollectionViewLayoutAttributes]() //レイアウトを準備するメソッド override func prepareLayout() { //全体の幅 let allWidth = CGRectGetWidth(collectionView!.bounds) - collectionView!.contentInset.left - collectionView!.contentInset.right //列の幅 let columnWidth = allWidth / CGFloat(numberColumns) //座標 var y:CGFloat = 0 var x:CGFloat = 0 //要素数ぶんループ for count in 0 ..< collectionView!.numberOfItemsInSection(0) { let indexPath = NSIndexPath(forItem:count, inSection:0) //レイアウトの配列に位置とサイズを登録する。 let frame = CGRect(x:x, y:y, width:columnWidth, height: height) let attributes = UICollectionViewLayoutAttributes(forCellWithIndexPath: indexPath) attributes.frame = frame layoutData.append(attributes) //X座標を更新 if(count % 2 == 0) { x = columnWidth } else { x = 0 } //Y座標を更新 y = y + height } } //レイアウトを返すメソッド override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? { return layoutData } //全体サイズを返すメソッド override func collectionViewContentSize() -> CGSize { //全体の幅 let allWidth = CGRectGetWidth(collectionView!.bounds) - collectionView!.contentInset.left - collectionView!.contentInset.right //全体の高さ let allHeight = CGFloat(collectionView!.numberOfItemsInSection(0)) * height return CGSize(width:allWidth, height:allHeight) } } |
ViewController.swiftは修正無し。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
// // ViewController.swift // import UIKit class ViewController: UIViewController, UICollectionViewDataSource { //データの個数を返すメソッド func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return 30 } //データを返すメソッド func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { //コレクションビューから識別子「TestCell」のセルを取得する。 let cell = collectionView.dequeueReusableCellWithReuseIdentifier("TestCell", forIndexPath: indexPath) as! TestCollectionViewCell //セルの背景色をランダムに設定する。 cell.backgroundColor = UIColor(red: CGFloat(drand48()), green: CGFloat(drand48()), blue: CGFloat(drand48()), alpha: 1.0) //セルのラベルに番号を設定する。 cell.testLabel.text = String(indexPath.row + 1) return cell } //最初からあるメソッド override func viewDidLoad() { super.viewDidLoad() } } |
以下は実際のプレイ動画