見出し画像

【徒然iOS】気ままにUIKit36〜UICollectionViewLayout〜

概要

このマガジンは四十を過ぎたおっさんが、

を参考にStoryboardでiOSアプリを完全に趣味で楽しんでいるだけな記事を気ままに上げてます。

今回

をやる〜〜〜

前準備

  1. 念の為、バックアップ

  2. 新しいクラス

  3. ビューコントローラの追加

  4. イニシャルビューの変更

をいつも通りやってから本題へ💃

こんな感じかな

本題

コレクションビューレイアウトとは、

コレクションビューに表示するセルのレイアウトを

自由にカスタマイズする

ために利用されるクラス

⒈前々回のビューの続きなので、前々回の新しいビューを作る

こんな感じかな💦
あ、MyのつもりがMtになってるけどまいいか!

⒉自作レイアウトのクラスを作る

CocoaTouchClassを選んで、Next
MyUICollectionViewLayoutとUICollectionViewLayoutを選んでNext
同一Target内であることを確認して、Create

⒊コレクションビューのアイデンティティインスペクタ設定項目のLayoutに「Custom」、Classに「TestCollectionViewLayout」を入力

こんな感じかな💦

⒋MyUICollectionViewLayoutクラスにコードを組み込む

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)
    }
}
コードをベタ貼りするとこんな感じ
Fixである程度は修正できたけど、
スーパークラスからメソッドをオーバーライドができないぞ
と言ってきてる
単純にオーバーライドしなければいいじゃん
って感じでoverride句を外してみる

一旦、シミュレータ実行

スクリーンショットではわかりにくいけど、このままだとスクロールもできない💦

なので、JumptoDefinition

を開いてSizeを検索

👉スーパークラス=適用してるプロトコル内のメソッドである可能性が高い

で実際よくみると、すでにiOS7.0からそもそもfunc=メソッドではなく、プロパティに変わってることがわかる

なので

    //全体サイズを返すメソッド
    override var collectionViewContentSize: CGSize {
        //全体の幅
        let allWidth = CGRectGetWidth(collectionView!.bounds) - collectionView!.contentInset.left - collectionView!.contentInset.right
        //全体の高さ
        let allHeight = CGFloat(collectionView!.numberOfItems(inSection: 0)) * height
        return CGSize(width:allWidth, height:allHeight)
    }

に書き方を変更して実行すると

下までスクロール出来るようになった🕺

例の如くラベルが表示されてないので、CollectionViewCellを

てな感じで大きくしてから

AutoLayoutの制約を前回までと同様に追加していく

黄色三角をクリックすると出てくるメッセージに従って
ガンガン右下のボタンをクリックして
はい消えた🕺

⒌シミュレータを実行

とまだ出てこない、、、

理由は実は簡単で、、、

カスタムレイアウトで設定してる幅と高さよりも外側にラベルが配置されているから 藁
(だったら端折らずに最初から書いとけよって話なんだけど、自分の中で繋がってる操作や設計を端折って説明するからこういうサイト記事や書籍はマジで多い)

なので、

ラベルをセルの右上端に移動して、再度、制約の黄色警告が出てくるので
update constraintsを選んで、右下のボタンをクリック
警告が消えたのを確認して〜〜〜〜

シミュレータ再実行

はい、完了🕺

⒍ブラッシュアップ

は、AutoLayoutなんかもここまででやっているので前回同様、今回も割愛🙇

以上。
ここまでの操作を見ればわかると思うけど、
参照記事では、前々回の続き〜〜〜
とか言いながら、実は全然、続きではないのでは?
って感じ。
(*説明を端折りすぎるとこうなるね〜〜〜〜)

今回のコードまとめ

ViewController側

import UIKit

class CollectionLayoutViewController: UIViewController,UICollectionViewDataSource {
    //データの個数を返すメソッド
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 30
    }
    //データを返すメソッド
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        //コレクションビューから識別子「TestCell」のセルを取得する。
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MyColletionLayoutCell", for: indexPath) as! MtCollectionLayoutViewCell
        //セルの背景色をランダムに設定する。
        cell.backgroundColor = UIColor(
            red: CGFloat(drand48()),
            green: CGFloat(drand48()),
            blue: CGFloat(drand48()),
            alpha: 1.0
        )
        //セルのラベルに番号を設定する。
        cell.myCollectionLayoutLabel.text = String(indexPath.row + 1)
        return cell
    }
    override func viewDidLoad() {
        super.viewDidLoad()
    }
}

CocoaTouchClass側

import UIKit

class MyUICollectionViewLayout: UICollectionViewLayout {
    let numberColumns = 2 //列数
    let height:CGFloat = 50 //セルの高さ
    //レイアウト配列
    private var layoutData = [UICollectionViewLayoutAttributes]()
    //レイアウトを準備するメソッド
    override func prepare() {
        //全体の幅
        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!.numberOfItems(inSection: 0) {
            let indexPath = NSIndexPath(item:count, section:0)
            //レイアウトの配列に位置とサイズを登録する。
            let frame = CGRect(x:x, y:y, width:columnWidth, height: height)
            let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath as IndexPath)
            attributes.frame = frame
            layoutData.append(attributes)
            //X座標を更新
            if(count % 2 == 0) {
                x = columnWidth
            } else {
                x = 0
            }
            //Y座標を更新
            y = y + height
        }
    }
    //レイアウトを返すメソッド
    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        return layoutData
    }
    //全体サイズを返すメソッド
    override var collectionViewContentSize: CGSize {
        //全体の幅
        let allWidth = CGRectGetWidth(collectionView!.bounds) - collectionView!.contentInset.left - collectionView!.contentInset.right
        //全体の高さ
        let allHeight = CGFloat(collectionView!.numberOfItems(inSection: 0)) * height
        return CGSize(width:allWidth, height:allHeight)
    }
}

今回の教訓

設計を考えて、コードを組むのが基本だから、コードから見てラベルを配置する!
とか言いつつ、簡単なテクニックで、結構、柔軟に修正対応はできる。
=それがオブジェクト指向言語の旨味だったりする。

⒈コードを覚える暇があったら、
オブジェクト指向言語の旨味を最大限に活かす方法や考え方を身につける

コードを丸暗記して理解してみたいな感じで階段型でやるのがSEとかレガシー言語からエンジニア気取ってる人は多いけど、そういう人ほど、こういうサンプルコードを見たことがないから、サンプルさえ知っていて、コードの修正方法の調べ方さえわかれば簡単にできることすら、何週間も悩んでいたりする
👉それで納期ギリギリか余計な予算をクライアントが追加したら意味ないんだけどね。

⒉百のコード解析よりも、まずはひとつでも多くサンプルコードに触れる
*進化し続ける最新のプログラミング言語では特に、、、。

Apple公式

さて、次回は

コレクションビューもこれでラストだねえ〜〜〜!

明日は、祝日だから休むし、来週から平日は、短期のアルバイトを入れたから、週末に2記事ずつくらいのペースにしばらくなるかも〜〜〜〜!

この記事が気に入ったらサポートをしてみませんか?