Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adaptation AppKit #78

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
Open

Adaptation AppKit #78

wants to merge 12 commits into from

Conversation

Mx-Iris
Copy link

@Mx-Iris Mx-Iris commented Sep 21, 2024

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!

# Conflicts:
#	ChatLayout/Classes/Core/ChatLayoutAttributes.swift
#	ChatLayout/Classes/Core/ChatLayoutInvalidationContext.swift
#	ChatLayout/Classes/Core/ChatLayoutSettings.swift
#	ChatLayout/Classes/Core/CollectionViewChatLayout.swift
#	ChatLayout/Classes/Core/Model/ItemKind.swift
#	ChatLayout/Classes/Core/Model/StateController.swift
#	ChatLayout/Classes/Extras/CellLayoutContainerView.swift
#	ChatLayout/Classes/Extras/ContainerCollectionReusableView.swift
#	ChatLayout/Classes/Extras/EdgeAligningView.swift
#	ChatLayout/Classes/Extras/Extensions/NSLayoutAnchor+Extension.swift
#	ChatLayout/Classes/Extras/Extensions/NSLayoutDimension+Extension.swift
#	ChatLayout/Classes/Extras/MessageContainerView.swift
#	ChatLayout/Classes/Extras/StaticViewFactory.swift
#	ChatLayout/Classes/Extras/SwappingContainerView.swift
#	Example/Tests/MockUICollectionViewUpdateItem.swift
@ekazaev
Copy link
Owner

ekazaev commented Sep 23, 2024

@Mx-Iris Hey. Great job. Thank you as well. Ill review it later this week. But by when I see looks great.

@Mx-Iris
Copy link
Author

Mx-Iris commented Sep 24, 2024

@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.

@Mx-Iris
Copy link
Author

Mx-Iris commented Sep 24, 2024

@ekazaev I renamed all types with the UI prefix to NSUI today, without the prefix it might conflict with SwiftUI's type

@Mx-Iris
Copy link
Author

Mx-Iris commented Sep 27, 2024

@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?

@Mx-Iris
Copy link
Author

Mx-Iris commented Sep 27, 2024

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

@Mx-Iris
Copy link
Author

Mx-Iris commented Sep 27, 2024

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

@Mx-Iris
Copy link
Author

Mx-Iris commented Sep 27, 2024

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.

@ekazaev
Copy link
Owner

ekazaev commented Oct 2, 2024

@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 :(

@Mx-Iris
Copy link
Author

Mx-Iris commented Oct 4, 2024

@ekazaev It's okay. Just leave it to me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants