I want to be able to retrieve the frame of a section in the collection view, however, I can't see a way to do that at the moment. Normally I would use the following code to retrieve the frame and check if it's "visible enough" so that I can play a video:
let attributes = collectionView.layoutAttributesForItem(at: indexPath)!
let cellRect = attributes.frame
let cellRectInSV = collectionView.convert(rect: cellRect, to: collectionView.superview)
let completelyVisible = collectionView.frame.contains(CGPoint(x: cellRectInSV.midX, y: CellRectInSV.midY))
With IGListKit I'm retrieving the indexPath like this:
let index = Int(adapter.section(for: sectionController))
let indexPath = IndexPath(row: 0, section: index)
But if the section has multiple cells then that index path will not be correct. Is there a better way to do this?
Hmm this could be easier w/ a helper method on IGListAdapter.
But I think you could just do:
let items = sectionController.numberOfItems()
let section = adapter.section(for: sectionController)
let first = IndexPath(item: 0, section: section)
let last = IndexPath(item: items - 1, section: section)
let firstFrame = collectionView.layoutAttributesForItem(at: first)!.frame
let lastFrame = collectionView.layoutAttributesForItem(at: last)!.frame
let sectionFrame = firstFrame.union(lastFrame)
Granted this doesn't include supplementary views or section insets.
Yep that works, cheers!
This seems like a good solution for this case, but note that for a standard UICollectionViewFlowLayout, the union of the first and last item frames within a section will not in general produce a region that intersects all item frames within the section. As an example, consider a 2-column grid with 3 items, the first 2 of which are in the first row. The union of first and last item frames produces the first column, which excludes the second item.
@rnystrom I'm trying to do similar behavior. Saw that you recommended more using IGListAdapter methods. Which API do you recommend the most for analyzing frames ?
visibleObjects, visibleSectionControllers ?
I really like the Instagram behavior.
So far, the solution solution I had in mind is :
scrollViewDelegate inside the main view controller.visibleSectionControllers from the adapter.visibleCells for section controller, called using collectionContext reference on each sectionController).I think that this solution would work. But I'd rather ask here if it's a good solution to go with. Want to make sure to implement the cleanest solution. I'm a little concerned about analyzing/comparing frames anytime the user scroll. Is it okay, or maybe too expensive ?
@rnystrom As follow up of my previous comment. I played around with my solution, and it's too expensive to make that solution in the main thread. In fact, scrollViewDidScroll gets called too many times, and doing the operations below seems too much :
It feels like the scroll experience gets weird now. I assume that it's too much for the main thread that s why it blocks it a little bit.
Then... I tried to make those operations in background thread, but it's not possible because some IGListKit APIs requires that they get called only in main thread.
Any suggestions how to implement Instagram like experience ? If Instagram is using IGListKit, I'm very interested on what was the approach to identify which video should be played if there are two videos appearing in the collection view.
@manonthemoon42 We use the scrollViewDelegate to federate out scroll events to various handlers and delegates that perform different operations, one of them looking for video cells and calculating auto play.
We don't actually rely on section controllers at all for video playback though, instead we just grab the visibleCells and filter ones that conform to a video player protocol. Whichever is the most visible is told to play.
Doing the frame math is really really simple:
collectionView.boundscell.frame of each visible, video-playing cellCGRectIntersection(cell.frame, collectionView.bounds).size.height / cell.frame.size.heightIn fact, scrollViewDidScroll gets called too many times, and doing the operations below seems too much
What behavior were you seeing? Did you do some profiling to see what the stalls were?
All of our scroll view operations and stuff is done on the main thread, all using IGListKit, and maintaining 60fps. So its definitely possible!
Very curious about the bottlenecks you ran into.
I didn't have a chance to do any profiling. I assumed directly that my approach was too expensive.
I over complicated my logic. I was first getting the section controllers, and then getting the visible cells from them. So I was making unnecessary iterations when I could get the visible cells directly from the collection view API.
I've implemented a solution based on your latest comment, and it works perfectly, it's really amazing.
Now, i'm getting the visible video cells by using array's filter method. Then getting the most visible cell by using array's method max.
From there, I play the video, and stop the already playing video(s).
Thank you @rnystrom for your response! I Really love 鉂わ笍 IGListKit 鉂わ笍 !!
@rnystrom , actually... I spoke a bit too fast about my scrolling issues ^^.
The scrolling was okay in simulator only. Then I tested in simulator, scrolled pretty fast, and there was some blocking UI effect. It gets blocks for a very short of time, but still provides a weird effect.
The frame calculations works very well, and doesn't impact the scrolling experience. So far, this piece works perfectly.
I think the reason why I have that problem, it's my usage of AVPlayer. I read online that creation of AVPlayerItem, or some operations like play, seek are executed in the main thread and consumes FPS. I checked using Time Profiler and AVPlayer gets executed in main thread even if it's wrapped into a Dispatch queue background. Screenshot below :

To resolve that, the first thing I thought about, it's to create my AVPlayerItem and making the AVPlayer operations in the background thread, but it didn't resolves the issue. Figured online that all those actions are still executed in main thread no matter what internally. After investigation on Stackoverflow, Reddit, Google, seems like many other engineers faces the same issue and it seems very tricky to resolve. Many people recommends using AsyncDisplayKit to resolve that problem.
For Instagram, do you guys use directly AVFoundation, or like Facebook app, you guys also uses AsyncDisplayKit - ASVideoNode for the videos ?
In advance, I understand if it's an information that cannot be shared since it's not IGListKit related.
@manonthemoon42 ahhhhh ya, good ol AVFoundation.... I honestly don't think I can get too specific on our video playing techniques 馃檴 however it _is possible_ to still get super buttery smooth scrolling w/ AVFoundation. Just have to make sure you're not overplaying (maybe reuse players/views) and to download/preprocess as much data as possible beforehand.
Most helpful comment
@manonthemoon42 We use the
scrollViewDelegateto federate out scroll events to various handlers and delegates that perform different operations, one of them looking for video cells and calculating auto play.We don't actually rely on section controllers at all for video playback though, instead we just grab the
visibleCellsand filter ones that conform to a video player protocol. Whichever is the most visible is told to play.Doing the frame math is really really simple:
collectionView.boundscell.frameof each visible, video-playing cellCGRectIntersection(cell.frame, collectionView.bounds).size.height / cell.frame.size.heightWhat behavior were you seeing? Did you do some profiling to see what the stalls were?
All of our scroll view operations and stuff is done on the main thread, all using IGListKit, and maintaining 60fps. So its definitely possible!
Very curious about the bottlenecks you ran into.