Horizontal scroll UICollectionView showing tags (custom flow layout)

Refresh

December 2018

Views

1.3k time

3

I've been trying to create a collection view that presents tags horizontally, in two row. The user can than scroll horizontally to view more tags. Exactly like the filters at the Bandcamp app https://itunes.apple.com/us/app/bandcamp/id706408639?mt=8

I found a very good tutorial on how to do something similar, by customizing UICollectionViewFlowLayout. https://codentrick.com/create-a-tag-flow-layout-with-uicollectionview/

However, this tutorial is meant for a vertical collection view, creating rows as needed. What I need is for the tags to flow right, and constrain the layout to two rows.

This is the snippet of the UICollectionViewFlowLayout from the tutorial

class FlowLayout: UICollectionViewFlowLayout {


  override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
    let attributesForElementsInRect = super.layoutAttributesForElementsInRect(rect)
    var newAttributesForElementsInRect = [UICollectionViewLayoutAttributes]()

    var leftMargin: CGFloat = 0.0;

    for attributes in attributesForElementsInRect! {
        if (attributes.frame.origin.x == self.sectionInset.left) {
            leftMargin = self.sectionInset.left
        } else {
            var newLeftAlignedFrame = attributes.frame
            newLeftAlignedFrame.origin.x = leftMargin
            attributes.frame = newLeftAlignedFrame
        }
        leftMargin += attributes.frame.size.width + 8
        newAttributesForElementsInRect.append(attributes)
    }

    return newAttributesForElementsInRect
  }
}

Thx!

2 answers

3

Итак, я, наконец, удалось закодировать решение моей проблемы: на prepareLayout, я переназначить X / Y позицию моих клеток, а также ширину моего содержания, то я обновить атрибуты LayoutAttributes. Хитрость заключается в том, чтобы иметь переменную, которая содержит информацию для обоих рядов, и изменить каждую ячейку соответственно. Вот код для тех, кто столкнулся с подобной проблемой

import UIKit

class FlowLayout: UICollectionViewFlowLayout {

// SET SCROLL TO HORIZONTAL
override init(){
    super.init()
    scrollDirection = .Horizontal
}

required init(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)!
    self.scrollDirection = .Horizontal
}

// SET VARIABLES: Content height/width and layout attributes
private var contentWidth: CGFloat  = 0.0
private var contentHeight: CGFloat  = 50.0
private var cache = [UICollectionViewLayoutAttributes]()


override func prepareLayout() {
    super.prepareLayout()

    // RUNS ONLY ONCE
    if cache.isEmpty {
        var row = 0
        let numberOfRows = 2
        var rowWidth = [CGFloat](count: numberOfRows, repeatedValue: 0)
        var xOffset = [CGFloat](count: numberOfRows, repeatedValue: 0)

        for item in 0 ..< collectionView!.numberOfItemsInSection(0) {
            let indexPath = NSIndexPath(forItem: item, inSection: 0)
            let tag = super.layoutAttributesForItemAtIndexPath(indexPath)
            let attributes = UICollectionViewLayoutAttributes(forCellWithIndexPath: indexPath)

            attributes.frame = tag!.frame
            attributes.frame.origin.x = xOffset[row]
            cache.append(attributes)

            rowWidth[row] = rowWidth[row] + tag!.frame.size.width + 8
            xOffset[row] = xOffset[row] + tag!.frame.size.width + 8
            row = row >= (numberOfRows - 1) ? 0 : 1
        }
        contentWidth = rowWidth.maxElement()!
    }
}

// SETS DYNAMIC WIDTH BASED ON TAGS
override func collectionViewContentSize() -> CGSize {
    return CGSize(width: contentWidth, height: contentHeight)
}

// UPDATES CELL ATTRIBUTES
override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
    var layoutAttributes = [UICollectionViewLayoutAttributes]()
    for attributes  in cache {
        if CGRectIntersectsRect(attributes.frame, rect ) {
            layoutAttributes.append(attributes)
        }
    }
    return layoutAttributes
}

override func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool {
    return true
}


}
0

Модифицированный код, чтобы быть правильным выровненным

class FlowLayout: UICollectionViewFlowLayout {

    override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        let attributesForElementsInRect = super.layoutAttributesForElementsInRect(rect)
        var newAttributesForElementsInRect = [UICollectionViewLayoutAttributes]()

        var rightMargin: CGFloat = rect.width;

        for attribute in attributesForElementsInRect! {
            attribute.frame.origin.x = rightMargin - attribute.frame.width - 8

            if attribute.frame.origin.x <= self.sectionInset.left {
                rightMargin = rect.width - self.sectionInset.right
                attribute.frame.origin.x = rightMargin - attribute.frame.width
            }
            rightMargin = rightMargin - attribute.frame.size.width - 8
            newAttributesForElementsInRect.append(attribute)
        }

        return newAttributesForElementsInRect
    }
}