UICollectionViewCell cellSize не работает для последнего элемента - PullRequest
2 голосов
/ 21 октября 2019

Я пытаюсь скопировать AppStore. Сегодняшний макет представления карты

Вот мой код

extension TestViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return 4
}

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

    if let label = genericCell.viewWithTag(100) as? UILabel {
        label.text = "\(indexPath.item)"
    }
    return genericCell
}
}

extension TestViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
    return 0
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
    return 40
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    let cellHeight: CGFloat = 100

    let numberOfItemsInRow = 2
    let compressedWidth = collectionView.bounds.width / 2
    let expandedWidth = compressedWidth * 0.5
    let rowNumber = indexPath.item / numberOfItemsInRow
    let isEvenRow = rowNumber % 2 == 0
    let isFirstItem = indexPath.item % numberOfItemsInRow == 0

    var width: CGFloat = 0.0
    if isEvenRow {
        width = isFirstItem ? expandedWidth : compressedWidth
    } else {
        width = isFirstItem ? compressedWidth : expandedWidth
    }
    return CGSize(width: width, height: cellHeight)
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
    return UIEdgeInsets(top: 20, left: 20, bottom: 0, right: 20)
}
}

Приведенный выше код будет работать нормально для нечетного числа источника данных. Если это четное число, последний ряд элемента не будет иметь никакого белого интервала между ними.

enter image description here

Способ, который я использую для решения этой проблемы, заключается в добавлении дополнительной 1 ячейки с автоматическим размером ширины, но с 0 высотой.

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

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    let cellHeight: CGFloat = indexPath.item == collectionView.numberOfItems(inSection: 0) - 1 ? 0 : 100 
}

enter image description here Однако я не думаю, что это лучший подход для использования. Кто-нибудь может подсказать мне, что делать? Что мне здесь не хватает? Спасибо

1 Ответ

0 голосов
/ 21 октября 2019

Лучший способ для вас - использовать пользовательский UICollectionViewFlowLayout

class TestCollectionViewLayout: UICollectionViewFlowLayout {

    var needUpdate: Bool = false

    fileprivate var cache = [UICollectionViewLayoutAttributes]()

    fileprivate var contentHeight: CGFloat = 0

    fileprivate var contentWidth: CGFloat { return collectionView?.frame.width ?? 0.0 }

    override var collectionViewContentSize: CGSize { return CGSize(width: contentWidth, height: contentHeight) }

    override func invalidateLayout() {
        needUpdate = true
        super.invalidateLayout()
    }

    override func prepare() {

        guard let collectionView = collectionView else { return }

        guard cache.isEmpty || needUpdate else {
            return
        }

        needUpdate = false

        cache = []

        contentHeight = 0

        let interitemSpacing: CGFloat = resolveMinimumInteritemSpacingForSectionAt(0)

        var yOffset: CGFloat = 0.0

        var previousItemSize: CGSize!

        for item in 0 ..< collectionView.numberOfItems(inSection: 0) {

            let indexPath = IndexPath(item: item, section: 0)

            let itemSize = resolveSizeForItem(at: indexPath)

            var point: CGPoint

            let isEvenColumn = item % 2 == 0

            if isEvenColumn {
                point = CGPoint(x: 0.0, y: yOffset + interitemSpacing)
            } else {
                point = CGPoint(x: previousItemSize.width + interitemSpacing, y: yOffset + interitemSpacing)
            }

            let frame = CGRect(origin: point, size: itemSize)

            let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
            attributes.frame = frame
            cache.append(attributes)

            contentHeight = max(contentHeight, frame.maxY)

            previousItemSize = frame.size

            if !isEvenColumn {
                yOffset = frame.maxY
            }
        }
    }

    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        var visibleLayoutAttributes = [UICollectionViewLayoutAttributes]()
        for attributes in cache {
            if attributes.frame.intersects(rect) {
                visibleLayoutAttributes.append(attributes)
            }
        }
        return visibleLayoutAttributes
    }

    override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        return cache[indexPath.item]
    }

}

extension TestCollectionViewLayout {

    func resolveCollectionView<T>(_ closure: (UICollectionView) -> T?, defaultValue: T) -> T {
        if let collectionView = collectionView {
            return closure(collectionView) ?? defaultValue
        } else {
            return defaultValue
        }
    }

    func resolveSizeForItem(at indexPath: IndexPath) -> CGSize {
        let size = resolveCollectionView({ collectionView -> CGSize? in
            return (collectionView.delegate as? UICollectionViewDelegateFlowLayout)?.collectionView?(collectionView,
                                                                                                   layout: self,
                                                                                                   sizeForItemAt: indexPath)
        }, defaultValue: CGSize(width: 150.0, height: 100.0))

        return size
    }

    func resolveMinimumInteritemSpacingForSectionAt(_ section: Int) -> CGFloat {
        let space = resolveCollectionView({ collectionView -> CGFloat? in
            return (collectionView.delegate as? UICollectionViewDelegateFlowLayout)?.collectionView?(collectionView,
                                                                                                   layout: self,
                                                                                                   minimumInteritemSpacingForSectionAt: section)
        }, defaultValue: 10.0)

        return space
    }
}

Реализовать 2 функции из UICollectionViewDelegateFlowLayout

extension TestViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
    return 10
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    let cellHeight: CGFloat = 100

    switch indexPath.item {
    case 0:
        return CGSize(width: (collectionView.bounds.width - 10) * 0.25, height: cellHeight)
    case 1:
    return CGSize(width: (collectionView.bounds.width - 10) * 0.75, height: cellHeight)
    case 2:
    return CGSize(width: (collectionView.bounds.width - 10) * 0.4, height: cellHeight)
    case 3:
    return CGSize(width: (collectionView.bounds.width - 10) * 0.6, height: cellHeight)
    default:
        return CGSize.zero
    }
    }
}

использовать этот макет как обычный

let layout = TestCollectionViewLayout()
layout.scrollDirection = .vertical
collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
collectionView.delegate = self
collectionView.dataSource = self
...