簡體   English   中英

計算 CollectionView 馬賽克布局的單元格大小

[英]Calculating Size of Cell for CollectionView Mosaic Layout

我正在嘗試制作類似於 Google 的 Keep 應用的馬賽克集合視圖布局。 我對UICollectionViewLayout進行了子類化,類似於在網上找到的許多教程。 為了正確布局集合視圖單元格,調用 class 必須實現委托, HeightForCellAtIndexPath方法來獲取單元格的高度。 就我而言,我還獲得了單元格的寬度來創建 1、2 或 3 列布局。

在所有教程中,單元格內容的高度都是已知的,不需要計算。 就我而言,內容的大小是未知的,需要計算。 我嘗試了許多不同的計算方法,但沒有一個能完美運行。 我最近的嘗試需要創建一個CardContent class 並將其添加到 cellForItemAt 中單元格的contentView中,並在cellForItemAt中實例化一個CardContent實例以計算傳遞給布局HeightForCellAtIndexPath的內容的大小。

我確定我的方法存在很多問題,但據我所知,問題似乎在於多行標簽在HeightForCellAtIndexPath中的布局不正確,因為標簽沒有換成多行並保持為單行因此給了我不正確的contentView高度。

CardContentCell.swift

import UIKit

class CardContentCell: UICollectionViewCell {
    var todoList: TodoList! {
        didSet {
            self.backgroundColor = UIColor(todoList.color)
        }
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.layer.cornerRadius = 5.0
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

卡片內容.swift

編輯:添加createLineItem方法。 請參閱下面的答案。

class CardContent: UIStackView {

    var todoList: TodoList!

    var verticalItemSpacing: CGFloat = 10.0

    var cellWidth: CGFloat!


    init(todoList: TodoList, cellWidth: CGFloat = 0.0) {
        self.todoList = todoList
        self.cellWidth = cellWidth

        super.init(frame: CGRect(x: 0, y: 0, width: cellWidth, height: 0))

        self.axis = .vertical
        self.alignment = .fill
        self.distribution = .fill
        self.contentMode = .scaleToFill

        self.spacing = 10.0

        layoutContent()
    }

    required init(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func createTitleLabel(title: String) -> UILabel {
        let label = UILabel()
        label.text = title
        label.font = label.font.withSize(20.0)
        label.numberOfLines = 2
        label.lineBreakMode = .byTruncatingTail
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }

    func createItemLabel(text: String) -> UILabel {
        let label = UILabel()
        label.text = text
        label.font = label.font.withSize(17.0)
        label.numberOfLines = 3
        label.lineBreakMode = .byTruncatingTail
        label.translatesAutoresizingMaskIntoConstraints = false
        label.sizeToFit()
        return label
    }

    func createLineItem(text: String) -> UIStackView {
        let hstack = UIStackView()
        hstack.axis = .horizontal
        hstack.alignment = .fill
        hstack.distribution = .fillProportionally

        let imgView = createImgView(withFont: lineItemFont)
        let textLabel = createItemLabel(text: text)

        hstack.addArrangedSubview(imgView)
        hstack.addArrangedSubview(textLabel)

        return hstack
    }

    func layoutContent() {

        self.addArrangedSubview(createTitleLabel(title: todoList.title))

        for todo in todoList.todos.prefix(6) {
            let lineItem = createLineItem(text: todo.text)
            self.addArrangedSubview(lineItem)

        }
    }

}

MyCollectionView.swift

extension MyCollectionView: UICollectionViewDataSource {

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellID, for: indexPath) as! CardContentCell
        cell.todoList = todoLists[indexPath.row]
        let content = CardContent(todoList: cell.todoList)
        cell.contentView.addSubview(content)
        content.pinTopAndSides(to: cell.contentView) // See extension below

        return cell
    }
}


extension MyCollectionView: CardLayoutDelegate {
    func collectionView(_ collectionView: UICollectionView, HeightForCellAtIndexPath indexPath: IndexPath, cellWidth: CGFloat) -> CGFloat {

        let todoList = todoLists[indexPath.row]
        let stackView = CardContent(todoList: todoList, cellWidth: cellWidth)

        stackView.translatesAutoresizingMaskIntoConstraints = false

        stackView.setNeedsLayout()
        stackView.layoutIfNeeded()

        let size = stackView.frame.size

        return size.height
    }
}

extension UIView {

    func pinTopAndSides(to other: UIView) {
        translatesAutoresizingMaskIntoConstraints = false
        leadingAnchor.constraint(equalTo: other.leadingAnchor).isActive = true
        trailingAnchor.constraint(equalTo: other.trailingAnchor).isActive = true
        topAnchor.constraint(equalTo: other.topAnchor).isActive = true
    }
}

結果是,如果始終有 6 個行項目,則計算的高度始終為 230(在 2 列布局中)。 在下面的屏幕截圖中,單元格被着色,而內容的 rest 溢出。

集合視圖的屏幕截圖

除非有更好的解決方案,否則我的答案是不使用嵌套的水平UIStackview 這充滿了未知數並且難以診斷自動布局問題。 相反,我使用了UIView並添加了我自己的約束。

這是創建所述視圖的方法。 有趣的是,沒有人仔細研究我的問題,以至於在我急於復制和過去的過程中,我在原帖中省略了這個最關鍵的方法。 我將使用此方法的原始實現來更新問題以供參考。

func createLineItem(text: String) -> UIView {
    let view = UIView()

    let imgView = createImgView(withFont: lineItemFont)
    imgView.translatesAutoresizingMaskIntoConstraints = false

    let textLabel = createItemLabel(text: text)
    textLabel.translatesAutoresizingMaskIntoConstraints = false

    imgView.tintColor = self.textColor

    view.addSubview(imgView)
    view.addSubview(textLabel)

    imgView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
    imgView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true

    textLabel.leadingAnchor.constraint(equalTo: imgView.trailingAnchor, constant: 5.0).isActive = true
    textLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
    textLabel.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
    textLabel.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true

    return view
}

而且,對於HeightForCellAtIndexPath委托 function,將widthAnchor設置為單元格寬度提供了正確的單元格高度:

func collectionView(_ collectionView: UICollectionView, HeightForCellAtIndexPath indexPath: IndexPath, cellWidth: CGFloat) -> CGFloat {
    let stackView = CardContent(todoList: todoList)
    stackView.translatesAutoresizingMaskIntoConstraints = false
    stackView.widthAnchor.constraint(equalToConstant: cellWidth).isActive = true
    stackView.setNeedsLayout()
    stackView.layoutIfNeeded()
    let size = stackView.frame.size
    return size.height
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM