Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
355 views
in Technique[技术] by (71.8m points)

ios - UICollectionView remove section breaks with UICollectionViewFlowLayout

I have a dataset that is divided into multiple sections, however, I'd like to display this in a collectionView without breaks between sections. Here's an illustration of what I want to achieve:

Instead of:
0-0 0-1 0-2
0-3
1-0 1-1
2-0
3-0

I want:
0-0 0-1 0-2
0-3 1-0 1-1
2-0 3-0

I realize the solution likely lies with a custom UICollectionViewLayout subclass, but I'm not sure how to achieve something like this.

Thanks

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You are correct that you need to subclass UICollectionViewLayout. The essence to understand before starting is that you need to calculate at least position and size for every cell in the collection view. UICollectionViewLayout is just a structured way to provide that information. You get the structure, but you have to provide everything else yourself.

There are 4 methods you need to override:

  • prepare
  • invalidateLayout
  • layoutAttributesForItemAtIndexPath
  • layoutAttributesForElementsInRect

One trick is to cache the layout attributes in a lookup table (dictionary):

var cachedItemAttributes = [IndexPath: UICollectionViewLayoutAttributes]()

In prepare, you calculate the layout attributes for each indexPath in your collectionView:

override func prepare() {
    super.prepare()
    calculateAttributes()
}

In invalidateLayout you reset the cached layout attributes and recalculate them:

override func invalidateLayout() {
    super.invalidateLayout()
    cachedItemAttributes = [:]
    calculateAttributes()
} 

In layoutAttributesForItemAtIndexPath you use the lookup table to return the right layout attributes:

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

In layoutAttributesForElementsInRect you filter your lookup table for the elements within the specified rect:

override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
    return cachedItemAttributes.filter { rect.intersects($0.value.frame) }.map { $0.value }
}

The final piece of the puzzle is the actual calculation of the layout attributes. Here I will provide only pseudo-code:

func calculateAttributes() {
    // For each indexpath (you can get this from the collectionView property using numberOfSections and numberOfItems:inSection )
    // calculate the frame, i.e the origin point and size of each cell in your collectionView and set it with UICollectionViewLayoutAttributes.frame
    // There are many other properties on UICollectionViewLayoutAttributes that you can tweak for your layout, but frame is a good starting point from which you can start experimenting.
    // Add the layout attributes to the lookup table
    // end loop
}

To answer your question, here is pseudo-code to calculate the position of each cell:

// If width of cell + current width of row + spacing, insets and margins exceeds the available width
// move to next row.
// else
// cell origin.x = current width of row + interitem spacing
// cell origin.y = number of rows * (row height + spacing)
// endif

If you need your custom layout to be configurable, then either use UICollectionViewDelegateFlowLayout if the available signatures are sufficient, or define your own that inherits from UICollectionViewDelegateFlowLayout or UICollectionViewDelegate. Because your protocol inherits from UICollectionViewDelegateFlowLayout, which itself inherits from UICollectionViewDelegate, you can set it directly as the collectionView delegate in your viewcontroller. In your custom collection view layout you just need to cast the delegate from UICollectionViewDelegate to your custom protocol to use it. Remember to handle cases where the casting fails or where the protocol methods are not implemented by the delegate.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...