web-dev-qa-db-ja.com

自動レイアウトを使用したUICollectionViewヘッダーの動的な高さ

タイプUICollectionViewのヘッダーを持つUICollectionReusableViewがあります。
そこにはラベルがあり、その長さはユーザーの入力によって異なります。
したがって、ヘッダーのラベルおよびその他の要素の高さに応じて、ヘッダーのサイズを動的に変更する必要があります。

これは私の絵コンテです:

storyboard

このアプリを実行したときの結果:

result

19
joda

これは私を半日ほど夢中にさせました。これが最終的に機能したものです。

ヘッダーのラベルが動的にサイズ設定され、1行で折り返されるように設定されていることを確認してください

  1. ビューにラベルを埋め込みます。これは自動サイズ設定に役立ちます。 enter image description here

  2. ラベルの制約が有限であることを確認してください。例:ボトムラベルから再利用可能なビューまでの「より大きい」制約は機能しません。上の画像をご覧ください。

  3. ラベルを埋め込んだビューのサブクラスにアウトレットを追加します

    class CustomHeader: UICollectionReusableView {
        @IBOutlet weak var contentView: UIView!
    }
    
  4. 初期レイアウトを無効にする

    override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()
    
        collectionView.collectionViewLayout.invalidateLayout()
    }
    
  5. ヘッダーをレイアウトして適切なサイズを取得します

    extension YourViewController: UICollectionViewDelegate {
        func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
    
            if let headerView = collectionView.visibleSupplementaryViews(ofKind: UICollectionElementKindSectionHeader).first as? CustomHeader {
                // Layout to get the right dimensions
                headerView.layoutIfNeeded()
    
                // Automagically get the right height
                let height = headerView.contentView.systemLayoutSizeFitting(UILayoutFittingExpandedSize).height
    
                // return the correct size
                return CGSize(width: collectionView.frame.width, height: height)
            }
    
            // You need this because this delegate method will run at least 
            // once before the header is available for sizing. 
            // Returning zero will stop the delegate from trying to get a supplementary view
            return CGSize(width: 1, height: 1)
        }
    
    }
    
24
Joe Susnick

スイフト3

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize{
    return CGSize(width: self.myCollectionView.bounds.width, height: self.mylabel.bounds.height)
}

https://developer.Apple.com/reference/uikit/uicollectionviewdelegateflowlayout/1617702-collectionview

14

以下を実装することで達成できます。

ViewController:

class ViewController: UIViewController {
    @IBOutlet weak var collectionView: UICollectionView!

    // the text that you want to add it to the headerView's label:
    fileprivate let myText = "Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda."
}

extension ViewController: UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
        switch kind {
        case UICollectionElementKindSectionHeader:
            let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind,
                                                                             withReuseIdentifier: "customHeader",
                                                                             for: indexPath) as! CustomCollectionReusableView

            headerView.lblTitle.text = myText
            headerView.backgroundColor = UIColor.lightGray

            return headerView
        default:
            fatalError("This should never happen!!")
        }
    }

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 100
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "customCell", for: indexPath)

        cell.backgroundColor = UIColor.brown
        // ...

        return cell
    }
}

extension ViewController: UICollectionViewDelegateFlowLayout {
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
        // ofSize should be the same size of the headerView's label size:
        return CGSize(width: collectionView.frame.size.width, height: myText.heightWithConstrainedWidth(font: UIFont.systemFont(ofSize: 17)))
    }
}


extension String {
    func heightWithConstrainedWidth(font: UIFont) -> CGFloat {
        let constraintRect = CGSize(width: UIScreen.main.bounds.width, height: CGFloat.greatestFiniteMagnitude)
        let boundingBox = self.boundingRect(with: constraintRect, options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: [NSFontAttributeName: font], context: nil)

        return boundingBox.height
    }
}

カスタムUICollectionReusableView

class CustomCollectionReusableView: UICollectionReusableView {
    @IBOutlet weak var lblTitle: UILabel!

    override func awakeFromNib() {
        super.awakeFromNib()

        // setup "lblTitle":
        lblTitle.numberOfLines = 0
        lblTitle.lineBreakMode = .byWordWrapping
        lblTitle.sizeToFit() 
    }
}
6
Ahmad F

これがエレガントで最新のソリューションです。

他の人が述べたように、まず、ヘッダービューの最上部から最初のサブビューの最上部、最初のサブビューの最下部から2番目のサブビューの最上部など、および最後のサブビューの下部からヘッダービューの下部まで。そうして初めて、自動レイアウトはビューのサイズを変更する方法を知ることができます。

次のコードは、ヘッダービューの計算されたサイズを返します。

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {

    // Get the view for the first header
    let indexPath = IndexPath(row: 0, section: section)
    let headerView = self.collectionView(collectionView, viewForSupplementaryElementOfKind: UICollectionView.elementKindSectionHeader, at: indexPath)

    // Use this view to calculate the optimal size based on the collection view's width
    return headerView.systemLayoutSizeFitting(CGSize(width: collectionView.frame.width, height: UIView.layoutFittingExpandedSize.height),
                                              withHorizontalFittingPriority: .required, // Width is fixed
                                              verticalFittingPriority: .fittingSizeLevel) // Height can be as large as needed
}
0
Pim