

UICollectionViewを使ってセクションが複数ある画面を実装しています。  
セルの大きさを可変にするため、UICollectionViewFlowLayoutをカスタムして利用しています。

ヘッダーを表示するには、レイアウトを生成した際に以下の値を設定する認識です。

[code]

    let layout = UICollectionViewFlowLayout()
    layout.headerReferenceSize = CGSizeMake(10,10)
    
[/code]

上記を設定することで、以下のファンクションが呼ばれる認識です。

[code]

    func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {}
    
[/code]

以下のようにレイアウトにカスタムクラスを利用した際、上記のファンクションが呼ばれなくなってしまいました。

[code]

    let layout = CustomLayout()
    layout.headerReferenceSize = CGSizeMake(10,10)
    
[/code]

どこかでheaderReferenceSizeの値がクリアされてしまう等、何か原因がありますでしょうか？

ViewController.swift

[code]

    var myCollectionView : UICollectionView!
    
    var mySection: [String] = [“セクション１”,”セクション２”]
    
    override func viewDidLoad() {
        super.viewDidLoad()
    
        let layout = CustomLayout()
    
        // セクション毎のヘッダーサイズ
        layout.headerReferenceSize = CGSizeMake(self.view.frame.width, 100)
    
        layout.sectionInset = UIEdgeInsetsMake(8, 8, 8, 8)
    
        layout.minimumLineSpacing = 8
        layout.minimumInteritemSpacing = 8
    
        layout.maxColumn = 5
        layout.cellPattern.append((sideLength: 2,heightLength: 2,column: 0,row: 0))
        layout.cellPattern.append((sideLength: 1,heightLength: 1,column: 2,row: 0))
        layout.cellPattern.append((sideLength: 1,heightLength: 1,column: 3,row: 0))
        layout.cellPattern.append((sideLength: 1,heightLength: 1,column: 4,row: 0))
        layout.cellPattern.append((sideLength: 1,heightLength: 1,column: 2,row: 1))
        layout.cellPattern.append((sideLength: 1,heightLength: 1,column: 3,row: 1))
        layout.cellPattern.append((sideLength: 1,heightLength: 1,column: 4,row: 1))
    
        myCollectionView = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
    
        myCollectionView.registerClass(CustomUICollectionViewCell.self, forCellWithReuseIdentifier: "MyCell")
        myCollectionView.registerClass(CustomCollectionReusableView.self, forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "MySection")
    
        myCollectionView.delegate = self
        myCollectionView.dataSource = self
    
        self.view.addSubview(myCollectionView)
    
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    
    func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
        return mySection.count
    }
    
    func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
        print(“セル押下”)
    }
    
    func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        switch (section) {
        case 0:
            return 7
        case 1:
            return 7
        default:
            return 0
        }
    }
    
    func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {
    
        let headerView: CustomCollectionReusableView = collectionView.dequeueReusableSupplementaryViewOfKind(UICollectionElementKindSectionHeader, withReuseIdentifier: "MySection", forIndexPath: indexPath) as! CustomCollectionReusableView
    
        headerView.title?.text = mySection[indexPath.section]
    
        return headerView
    
    }
    
    func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
        let cell: CustomUICollectionViewCell = collectionView.dequeueReusableCellWithReuseIdentifier("MyCell", forIndexPath: indexPath) as! CustomUICollectionViewCell
    
        switch (indexPath.section) {
        case 0:
            cell.backgroundColor = UIColor.redColor()
            cell.textLabel?.text = "0"
            cell.imageTest.image = UIImage(named: "image01.jpg")
        case 1:
            cell.backgroundColor = UIColor.greenColor()
            cell.textLabel?.text = "1"
        default:
            print("section error")
            cell.backgroundColor = UIColor.whiteColor()
        }
        return cell
    }
    
[/code]

CustomUICollectionViewCell.swift

[code]

    var textLabel: UILabel!
    var imageTest: UIImageView!
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
    
        textLabel = UILabel(frame: CGRectMake(0,0,frame.width,frame.height))
        textLabel?.text = "nil"
        textLabel?.textAlignment = NSTextAlignment.Center
    
        imageTest = UIImageView(frame: CGRectMake(0,0,frame.width,frame.height))
    
        self.contentView.addSubview(imageTest)
        self.contentView.addSubview(textLabel)
    }
    
[/code]

CustomLayout.swift

[code]

    private static let maxRow = 3
    
    var maxColumn = maxRow
    var cellPattern:[(sideLength: CGFloat, heightLength: CGFloat, column: CGFloat, row:CGFloat)] = []
    
    private var sectionCells = [[CGRect]]()
    private var contentSize = CGSizeZero
    
    override func prepareLayout() {
        super.prepareLayout()
    
        sectionCells = [[CGRect]]()
    
        if let collectionView = self.collectionView {
            contentSize = CGSize(width: collectionView.bounds.width - collectionView.contentInset.left - collectionView.contentInset.right, height: 0)
            let smallCellSideLength: CGFloat = (contentSize.width - super.sectionInset.left - super.sectionInset.right - (super.minimumInteritemSpacing * (CGFloat(maxColumn) - 1.0))) / CGFloat(maxColumn)
    
            for section in (0..<collectionView.numberOfSections()) {
                var cells = [CGRect]()
                let numberOfCellInSection = collectionView.numberOfItemsInSection(section)
                var height = contentSize.height
    
                for i in (0..<numberOfCellInSection) {
                    let position = i % (numberOfCellInSection)
                    let cellPosition = position % cellPattern.count
                    let cell = cellPattern[cellPosition]
                    let x = (cell.column * (smallCellSideLength + super.minimumInteritemSpacing)) + super.sectionInset.left
                    let y = (cell.row * (smallCellSideLength + super.minimumLineSpacing)) + contentSize.height + super.sectionInset.top
                    let cellwidth = (cell.sideLength * smallCellSideLength) + ((cell.sideLength-1) * super.minimumInteritemSpacing)
                    let cellheight = (cell.heightLength * smallCellSideLength) + ((cell.heightLength-1) * super.minimumInteritemSpacing)
    
                    let cellRect = CGRectMake(x, y, cellwidth, cellheight)
                    cells.append(cellRect)
    
                    if (height < cellRect.origin.y + cellRect.height ) {
                        height = cellRect.origin.y + cellRect.height
                    }
                }
                contentSize = CGSize(width: contentSize.width, height: height)
                sectionCells.append(cells)
            }
        }
    }
    
    override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        var layoutAttributes = [UICollectionViewLayoutAttributes]()
    
        if let collectionView = self.collectionView {
            for i in (0..<collectionView.numberOfSections()) {
    
                let numberOfCellsInSection = collectionView.numberOfItemsInSection(i)
    
                for j in (0..<numberOfCellsInSection) {
                    let indexPath = NSIndexPath(forRow: j, inSection: i)
                    if let attributes = layoutAttributesForItemAtIndexPath(indexPath) {
                        if (CGRectIntersectsRect(rect, attributes.frame)) {
                            layoutAttributes.append(attributes)
                        }
                    }
                }
            }
        }
        return layoutAttributes
    }
    
    override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes? {
        let attributes = super.layoutAttributesForItemAtIndexPath(indexPath)
        attributes!.frame = sectionCells[indexPath.section][indexPath.row]
        return attributes
    }
    
    override func collectionViewContentSize() -> CGSize {
        return contentSize
    }
    
[/code]

CustomCollectionReusableView.swift

[code]

    var title: UILabel!
    var backgroundImage: UIImageView!
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
    
        title = UILabel(frame: CGRectMake(frame.width*0.05,0,frame.width,frame.height))
        title.text = "nil"
        title.textAlignment = NSTextAlignment.Left
        title.textColor = UIColor.redColor()
    
        let bgImage: UIImage = UIImage(named: "image02.jpg")!
        backgroundImage = UIImageView(frame: CGRectMake(0, 0, frame.width, frame.height))
        backgroundImage.image = bgImage
    
        self.addSubview(backgroundImage)
        self.addSubview(title)
    
    }
    
[/code]


CC BY-SA 3.0
2017-02-20T13:04:59.377
32814
2017-11-14T02:20:23.377
2017-02-21T03:58:22.430
20744
20744
question
swift
UICollectionViewのヘッダーについて


こちらのドキュメントはじっくり読まれましたでしょうか。

[Collection View
プログラミングガイド](https://developer.apple.com/jp/documentation/CollectionViewPGforIOS.pdf)

(pdfの日本語版よりHTMLの[英語版](https://developer.apple.com/library/content/documentation/WindowsViews/Conceptual/CollectionViewPGforIOS/Introduction/Introduction.html)の方が読みやすいかもしれません。)

その中の[こちら](https://developer.apple.com/library/content/documentation/WindowsViews/Conceptual/CollectionViewPGforIOS/UsingtheFlowLayout/UsingtheFlowLayout.html#//apple_ref/doc/uid/TP40012334-CH3-SW4):

> ## フローレイアウトのサブクラスを定義するべき状況

(表3-1中、 **シナリオ:補助ビューや装飾ビューを新たに追加したい** より)

>
> 標準の「フローレイアウト」クラスは、セクションヘッダやセクションフッタを補助ビューとして追加できるだけであり、装飾ビューにも対応していません。補助ビューや装飾ビューを追加したい場合、少なくとも次のメソッドをオーバーライドする必要があります。
>
>   * `layoutAttributesForElementsInRect:`(必須)
>   * `layoutAttributesForItemAtIndexPath:`(必須)
>   * `layoutAttributesForSupplementaryViewOfKind:atIndexPath:`(補助ビューの場合)
>   * `layoutAttributesForDecorationViewOfKind:atIndexPath:`(装飾ビューの場合)
>

あなたの場合、補助ビュー(Supplementary View)であるヘッダーを定義しようとしているので、以下の3つのメソッドを **正しく実装**
してやる必要(*1)があります。

  * `layoutAttributesForElementsInRect:`
  * `layoutAttributesForItemAtIndexPath:`
  * `layoutAttributesForSupplementaryViewOfKind:atIndexPath:`

(*1) 上記では **_オーバーライドする必要があります_** ( ** _you need to override_** ) ですが、今回の場合、
**新たに追加** ではないので、`UICollectionViewFlowLayout`に定義された動作がそのまま使える場合もあります。

あなたのコードでは以上3つの中で、`layoutAttributesForElementsInRect(_:)`の実装に問題があるのが目に付きます。

>
> `layoutAttributesForElementsInRect:`メソッドでは、`super`を呼び出してセルのレイアウト属性を取得し、指定された矩形内に置く、新しい補助ビューや装飾ビューの属性を追加します。他のメソッドには、必要に応じて属性を指定する働きがあります。

(上記の文章、どう贔屓目に見ても良い訳には見えないので、原文にも目を通しておくことをお勧めします。)

> In your `layoutAttributesForElementsInRect:` method, you can call `super` to
> get the layout attributes for the cells and then add the attributes for any
> new supplementary or decoration views that are in the specified rectangle.
> Use the other methods to provide attributes on demand.

(最後の文の拙訳: _必要に応じて属性を提供するための他のメソッドを使用してください。_ )

本題にとって重要な事柄は、`layoutAttributesForElementsInRect(_:)`では、
**補助ビューや装飾ビューのためのレイアウト属性も返さないといけない** と言うことです。

* * *

前提になるドキュメントの記述の紹介が長くなってしまいましたが、あなたのコードの`layoutAttributesForElementsInRect(_:)`では、セルに対するレイアウト属性しか返そうとしていませんので、結果的に`collectionView(_:viewForSupplementaryElementOfKind:indexPath:)`も呼ばれない、と言うことになっています。

上記のメソッドを例えば以下のように修正してみてください。

[code]

    override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        var layoutAttributes = [UICollectionViewLayoutAttributes]()
        
        if let collectionView = self.collectionView {
            for i in 0..<collectionView.numberOfSections() {
                
                let numberOfCellsInSection = collectionView.numberOfItemsInSection(i)
                
                for j in 0..<numberOfCellsInSection {
                    let indexPath = NSIndexPath(forRow: j, inSection: i)
                    if let attributes = layoutAttributesForItemAtIndexPath(indexPath) {
                        if (CGRectIntersectsRect(rect, attributes.frame)) {
                            layoutAttributes.append(attributes)
                        }
                    }
                    //補助ビューと装飾ビューのレイアウト属性も返す必要がある
                    //UICollectionViewFlowLayoutの場合はヘッダーとフッター(どちらも補助ビュー)
                    if let attributes = layoutAttributesForSupplementaryViewOfKind(UICollectionElementKindSectionHeader, atIndexPath: indexPath) {
                        if CGRectIntersectsRect(rect, attributes.frame) {
                            layoutAttributes.append(attributes)
                        }
                    }
                    if let attributes = layoutAttributesForSupplementaryViewOfKind(UICollectionElementKindSectionFooter, atIndexPath: indexPath) {
                        if CGRectIntersectsRect(rect, attributes.frame) {
                            layoutAttributes.append(attributes)
                        }
                    }
                }
            }
        }
        return layoutAttributes
    }
    
[/code]

少なくともご質問の本題の「 _ **上記のファンクション**
(`collectionView(_:viewForSupplementaryElementOfKind:indexPath:)`)
**が呼ばれなくなってしまいました**_ 」については、これで解消できるはずです。お試しください。


CC BY-SA 3.0
2017-02-21T14:23:02.360
32850
2017-02-21T14:23:02.360
2020-06-17T08:14:45.997
-1
13972
32814
answer