Open Mx-Iris opened 2 months ago
@Mx-Iris Hey. Great job. Thank you as well. Ill review it later this week. But by when I see looks great.
@ekazaev Thank you very much for your recognition. I think the main library is almost done. The example app is still being adapted and is expected to be completed this week. As for the documentation, you may need to make some modifications.
@ekazaev I renamed all types with the UI prefix to NSUI today, without the prefix it might conflict with SwiftUI's type
@ekazaev I found a very serious problem, the targetContentOffsetForProposedContentOffset
method of NSCollectionViewLayout
does not work, the returned offset is not applied, and the visibleBounds
will be incorrect after a while of forced scrolling to the specified position, so keepContentOffsetAtBottomOnBatchUpdates
this function is currently ineffective, do you have any better suggestions?
Here's a log, it just started working fine, but at one point the proposedContentOffset was less than the previous newProposedContentOffset, which seems to be the value of visibleBounds, and I'm now forcing the contentOffset to be set in the same way as the fix for iOS15, as the Returning directly doesn't work, and by the way, Catalyst doesn't seem to need this setting.
targetContentOffset(forProposedContentOffset:), controller.proposedCompensatingOffset: 0.0
targetContentOffset(forProposedContentOffset:), controller.proposedCompensatingOffset: 44.0
proposedContentOffset: (0.0, 1692.0), newProposedContentOffset: (0.0, 1736.0)
targetContentOffset(forProposedContentOffset:), controller.proposedCompensatingOffset: 0.0
targetContentOffset(forProposedContentOffset:), controller.proposedCompensatingOffset: 0.0
targetContentOffset(forProposedContentOffset:), controller.proposedCompensatingOffset: 44.0
proposedContentOffset: (0.0, 1736.0), newProposedContentOffset: (0.0, 1780.0)
targetContentOffset(forProposedContentOffset:), controller.proposedCompensatingOffset: 0.0
targetContentOffset(forProposedContentOffset:), controller.proposedCompensatingOffset: 44.0
proposedContentOffset: (0.0, 1764.0), newProposedContentOffset: (0.0, 1808.0)
targetContentOffset(forProposedContentOffset:), controller.proposedCompensatingOffset: 0.0
targetContentOffset(forProposedContentOffset:), controller.proposedCompensatingOffset: 44.0
proposedContentOffset: (0.0, 1800.0), newProposedContentOffset: (0.0, 1844.0)
targetContentOffset(forProposedContentOffset:), controller.proposedCompensatingOffset: 0.0
targetContentOffset(forProposedContentOffset:), controller.proposedCompensatingOffset: 44.0
proposedContentOffset: (0.0, 1840.0), newProposedContentOffset: (0.0, 1884.0)
targetContentOffset(forProposedContentOffset:), controller.proposedCompensatingOffset: 0.0
targetContentOffset(forProposedContentOffset:), controller.proposedCompensatingOffset: 44.0
proposedContentOffset: (0.0, 1848.0), newProposedContentOffset: (0.0, 1892.0)
targetContentOffset(forProposedContentOffset:), controller.proposedCompensatingOffset: 0.0
targetContentOffset(forProposedContentOffset:), controller.proposedCompensatingOffset: 0.0
targetContentOffset(forProposedContentOffset:), controller.proposedCompensatingOffset: 0.0
targetContentOffset(forProposedContentOffset:), controller.proposedCompensatingOffset: 0.0
targetContentOffset(forProposedContentOffset:), controller.proposedCompensatingOffset: 0.0
targetContentOffset(forProposedContentOffset:), controller.proposedCompensatingOffset: 0.0
This is the log that doesn't force the contentOffset to be set
itemFrameMaxY: 3191.0, visibleBoundsMaxY: 3199.0
targetContentOffset(forProposedContentOffset:), controller.proposedCompensatingOffset: 44.0
proposedContentOffset: (0.0, 2431.0), newProposedContentOffset: (0.0, 2475.0)
itemFrameMaxY: 3236.5, visibleBoundsMaxY: 3239.0
targetContentOffset(forProposedContentOffset:), controller.proposedCompensatingOffset: 0.5
proposedContentOffset: (0.0, 2471.0), newProposedContentOffset: (0.0, 2471.5)
itemFrameMaxY: 3236.5, visibleBoundsMaxY: 3239.0
targetContentOffset(forProposedContentOffset:), controller.proposedCompensatingOffset: 0.5
proposedContentOffset: (0.0, 2471.0), newProposedContentOffset: (0.0, 2471.5)
itemFrameMaxY: 3236.5, visibleBoundsMaxY: 3239.0
targetContentOffset(forProposedContentOffset:), controller.proposedCompensatingOffset: 0.5
proposedContentOffset: (0.0, 2471.0), newProposedContentOffset: (0.0, 2471.5)
itemFrameMaxY: 3236.5, visibleBoundsMaxY: 3239.0
targetContentOffset(forProposedContentOffset:), controller.proposedCompensatingOffset: 0.5
proposedContentOffset: (0.0, 2471.0), newProposedContentOffset: (0.0, 2471.5)
itemFrameMaxY: 3236.5, visibleBoundsMaxY: 3239.0
targetContentOffset(forProposedContentOffset:), controller.proposedCompensatingOffset: 0.5
proposedContentOffset: (0.0, 2471.0), newProposedContentOffset: (0.0, 2471.5)
itemFrameMaxY: 3236.5, visibleBoundsMaxY: 3239.0
targetContentOffset(forProposedContentOffset:), controller.proposedCompensatingOffset: 0.5
proposedContentOffset: (0.0, 2471.0), newProposedContentOffset: (0.0, 2471.5)
itemFrameMaxY: 3236.5, visibleBoundsMaxY: 3239.0
targetContentOffset(forProposedContentOffset:), controller.proposedCompensatingOffset: 0.5
proposedContentOffset: (0.0, 2471.0), newProposedContentOffset: (0.0, 2471.5)
itemFrameMaxY: 3236.5, visibleBoundsMaxY: 3239.0
targetContentOffset(forProposedContentOffset:), controller.proposedCompensatingOffset: 0.5
proposedContentOffset: (0.0, 2471.0), newProposedContentOffset: (0.0, 2471.5)
itemFrameMaxY: 3236.5, visibleBoundsMaxY: 3239.0
targetContentOffset(forProposedContentOffset:), controller.proposedCompensatingOffset: 0.5
proposedContentOffset: (0.0, 2471.0), newProposedContentOffset: (0.0, 2471.5)
itemFrameMaxY: 3236.5, visibleBoundsMaxY: 3239.0
targetContentOffset(forProposedContentOffset:), controller.proposedCompensatingOffset: 0.5
proposedContentOffset: (0.0, 2471.0), newProposedContentOffset: (0.0, 2471.5)
itemFrameMaxY: 3236.5, visibleBoundsMaxY: 3239.0
targetContentOffset(forProposedContentOffset:), controller.proposedCompensatingOffset: 0.5
proposedContentOffset: (0.0, 2471.0), newProposedContentOffset: (0.0, 2471.5)
itemFrameMaxY: 3236.5, visibleBoundsMaxY: 3239.0
targetContentOffset(forProposedContentOffset:), controller.proposedCompensatingOffset: 0.5
proposedContentOffset: (0.0, 2471.0), newProposedContentOffset: (0.0, 2471.5)
itemFrameMaxY: 3236.5, visibleBoundsMaxY: 3239.5
targetContentOffset(forProposedContentOffset:), controller.proposedCompensatingOffset: 0.0
itemFrameMaxY: 3236.5, visibleBoundsMaxY: 3239.5
targetContentOffset(forProposedContentOffset:), controller.proposedCompensatingOffset: 0.0
itemFrameMaxY: 3236.5, visibleBoundsMaxY: 3239.5
targetContentOffset(forProposedContentOffset:), controller.proposedCompensatingOffset: 0.0
itemFrameMaxY: 3236.5, visibleBoundsMaxY: 3239.5
targetContentOffset(forProposedContentOffset:), controller.proposedCompensatingOffset: 0.0
itemFrameMaxY: 3236.5, visibleBoundsMaxY: 3239.5
targetContentOffset(forProposedContentOffset:), controller.proposedCompensatingOffset: 0.0
itemFrameMaxY: 3236.5, visibleBoundsMaxY: 3239.5
targetContentOffset(forProposedContentOffset:), controller.proposedCompensatingOffset: 0.0
itemFrameMaxY: 3236.5, visibleBoundsMaxY: 3239.5
targetContentOffset(forProposedContentOffset:), controller.proposedCompensatingOffset: 0.0
Based on a series of reverse engineering, I found that after the method targetContentOffsetForProposedContentOffset:
of NSCollectionViewLayout
is called and returns, _NSCollectionViewCore
sets its own bounds. However, in UIKit, it is UICollectionView
that calls this method and similarly sets its own bounds. Since UICollectionView
itself is a UIScrollView
, the contentOffset
is correctly set. But I found that _NSCollectionViewCore
seems not to set the origin of NSClipView
or call the collectionView's scroll method. We can manually set the contentOffset before this method returns.
Another issue is that, after this series of methods ends, calling invalidateLayout, and then returning to the initial method _updateWithItems:tentativelyForReordering:...
, to update and create a series of layoutAttributes. The problem is that in UIKit, the frame.y/height
of the two layoutAttributes in the method invalidationContextForPreferredLayoutAttributes:withOriginalAttributes:
are different, whereas in AppKit, they are exactly the same, leading to the contentOffsetAdjustment
in the context not being set and remaining at 0. I feel the biggest issue currently lies here.
Welcome to discuss together, there is very little information and documentation about CollectionViewLayout
, and a lot of reverse engineering is needed to gain insights.
@Mx-Iris Hi. I am currently on my vacation. So sorry for making little input here. I actually know very little about AppKit since I never worked with the Mac applications. I am not sure If I can be a good help there :(
@ekazaev It's okay. Just leave it to me.
@ekazaev Hi, I'm sorry for not updating the progress for so long. I've been very busy with work lately. I've been researching for a long time without any progress, so I've put it on hold for now. I tried submitting a DTS to Apple, but they asked me to provide a reproducible demo. I don't have time to make these.
Currently, it seems that only this function has problems. You can review it first and let's consider these later.
@ekazaev Because AppKit cannot customize layout margins, the demo looks a bit strange. I will make time to finish them.
I tried to extend NSView with customizable layoutMargins, and the UI looks fine now. However, I found that the feature of scrolling to the bottom at launch in the demo is not working properly, so I am looking for a solution.
@Mx-Iris Good luck. Emulate also pushing into the navigation controller. It also usually shows issues.
Thank you very much for creating this repository, I really like to customize the collectionView using layout, UICollectionView and NSCollectionView are two very similar components, I did some adaptations to make it work on Mac, I'm going to write some test cases next, I have initially tested it to be able to run!