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 18 commits into
base: master
Choose a base branch
from
Open

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.

@ekazaev ekazaev self-requested a review September 23, 2024 14:48
@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.

@Mx-Iris
Copy link
Author

Mx-Iris commented Oct 28, 2024

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

@Mx-Iris
Copy link
Author

Mx-Iris commented Oct 28, 2024

@ekazaev Because AppKit cannot customize layout margins, the demo looks a bit strange. I will make time to finish them.

@Mx-Iris
Copy link
Author

Mx-Iris commented Oct 28, 2024

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.

@ekazaev
Copy link
Owner

ekazaev commented Nov 18, 2024

@Mx-Iris Good luck. Emulate also pushing into the navigation controller. It also usually shows issues.

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