iosswiftuicollectionviewuicollectionviewflowlayout

Setup a collectionView with "tag"-like cells


I've been working on with a custom UICollectionViewFlowLayout to adjust the spaces between the cells so I can get a nice flow in my collectionView. But with my current code I can't figure out how I can adjust the cell sizes without "breaking" rows count. Which makes some cells think they can fit a row but they can't.

My custom flowlayout

class UserProfileTagsFlowLayout: UICollectionViewFlowLayout {
    
    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        let attributesForElementsInRect = super.layoutAttributesForElements(in: 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 // Makes the space between cells
            newAttributesForElementsInRect.append(attributes)
        }
        
        return newAttributesForElementsInRect
    }
}

So in my ViewController I've extende

extension myViewController: UICollectionViewDelegateFlowLayout {
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let tag = tagsArray[indexPath.row]
        let font = UIFont(name: "Raleway", size: 14)!
        let size = tag.value.size(withAttributes: [NSAttributedString.Key.font: font])
        let dynamicCellWidth = size.width

        /*
          The "+ 20" gives me the padding inside the cell
        */
        return CGSize(width: dynamicCellWidth + 20, height: 35)
    }
    
    // Space between rows
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return 5
    }
    
    // Space between cells
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
        return 10
    }
}

When I try this code out i'm getting the result down in the result-section on an iPhone X. You can see the tag "Machine Learning" jumps down to a new row, because it thinks it can fit the row above. If I remove the "padding" for the cell in sizeForItem function, then I won't have this problem. But it just looks awful.

So my question is: How can I use padding on my cells without breaking the flowLayout?


So far my current result looks like this:

iPhone X:

iPhone X result


Solution

  • Maybe it will help you. I think you should check if you item stick out the collection view right inset. In layoutAttributesForElements method check this:

            let collectionViewWidth = self.collectionView?.frame.width - (self.sectionInset.right + self.sectionInset.left)
    
            if (attributes.frame.origin.x + attributes.frame.size.width > collectionViewWidth) {
                var newLeftAlignedFrame = attributes.frame
                newLeftAlignedFrame.origin.x = self.sectionInset.left
                attributes.frame = newLeftAlignedFrame 
            }
    

    Updated my answer and it works for me, you can see it on screenshot