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
Prev Previous commit
Next Next commit
Adaptive Example (80%)
  • Loading branch information
Mx-Iris committed Sep 25, 2024
commit 2edd8d319d6d35bb24e8adc7e0c177eaa3d5a315
37 changes: 20 additions & 17 deletions ChatLayout/Classes/Core/CollectionViewChatLayout.swift
Original file line number Diff line number Diff line change
@@ -43,7 +43,6 @@ import UIKit
/// `CollectionViewChatLayout.restoreContentOffset(...)`

open class CollectionViewChatLayout: NSUICollectionViewLayout {

// MARK: Custom Properties

/// `CollectionViewChatLayout` delegate.
@@ -107,8 +106,8 @@ open class CollectionViewChatLayout: NSUICollectionViewLayout {
return CGRect(
x: adjustedContentInset.left,
y: collectionView.contentOffset.y + adjustedContentInset.top,
width: collectionView.bounds.width - adjustedContentInset.left - adjustedContentInset.right,
height: collectionView.bounds.height - adjustedContentInset.top - adjustedContentInset.bottom
width: collectionView.scrollViewBounds.width - adjustedContentInset.left - adjustedContentInset.right,
height: collectionView.scrollViewBounds.height - adjustedContentInset.top - adjustedContentInset.bottom
)
}

@@ -121,7 +120,7 @@ open class CollectionViewChatLayout: NSUICollectionViewLayout {
return CGRect(
x: adjustedContentInset.left + additionalInsets.left,
y: adjustedContentInset.top + additionalInsets.top,
width: collectionView.bounds.width - additionalInsets.left - additionalInsets.right - adjustedContentInset.left - adjustedContentInset.right,
width: collectionView.scrollViewBounds.width - additionalInsets.left - additionalInsets.right - adjustedContentInset.left - adjustedContentInset.right,
height: controller.contentHeight(at: state) - additionalInsets.top - additionalInsets.bottom - adjustedContentInset.top - adjustedContentInset.bottom
)
}
@@ -180,7 +179,7 @@ open class CollectionViewChatLayout: NSUICollectionViewLayout {
guard let collectionView else {
return .zero
}
return collectionView.frame.size
return collectionView.scrollViewFrame.size
}

// MARK: Private Properties
@@ -284,9 +283,9 @@ open class CollectionViewChatLayout: NSUICollectionViewLayout {
}

let insets = NSUIEdgeInsets(
top: -collectionView.frame.height,
top: -collectionView.scrollViewFrame.height,
left: 0,
bottom: -collectionView.frame.height,
bottom: -collectionView.scrollViewFrame.height,
right: 0
)
let visibleBounds = visibleBounds
@@ -457,7 +456,7 @@ open class CollectionViewChatLayout: NSUICollectionViewLayout {
}

if prepareActions.contains(.cachePreviousWidth) {
cachedCollectionViewSize = collectionView.bounds.size
cachedCollectionViewSize = collectionView.scrollViewBounds.size
}

prepareActions = []
@@ -540,13 +539,15 @@ open class CollectionViewChatLayout: NSUICollectionViewLayout {
controller.process(changeItems: [])
state = .afterUpdate
prepareActions.remove(.switchStates)
guard let collectionView,
oldBounds.width != collectionView.bounds.width,

let newBounds = collectionView?.scrollViewBounds
guard let newBounds,
oldBounds.width != newBounds.width,
keepContentOffsetAtBottomOnBatchUpdates,
controller.isLayoutBiggerThanVisibleBounds(at: state) else {
return
}
let newBounds = collectionView.bounds

let heightDifference = oldBounds.height - newBounds.height
controller.proposedCompensatingOffset += heightDifference + (oldBounds.origin.y - newBounds.origin.y)
}
@@ -621,7 +622,7 @@ open class CollectionViewChatLayout: NSUICollectionViewLayout {
if heightDifference != 0,
(keepContentOffsetAtBottomOnBatchUpdates && controller.contentHeight(at: state).rounded() + heightDifference > visibleBounds.height.rounded()) || isUserInitiatedScrolling,
isAboveBottomEdge {
let offsetCompensation: CGFloat = min(controller.contentHeight(at: state) - collectionView!.frame.height + adjustedContentInset.bottom + adjustedContentInset.top, heightDifference)
let offsetCompensation: CGFloat = min(controller.contentHeight(at: state) - collectionView!.scrollViewFrame.height + adjustedContentInset.bottom + adjustedContentInset.top, heightDifference)
context.contentOffsetAdjustment.y += offsetCompensation
invalidationActions.formUnion([.shouldInvalidateOnBoundsChange])
}
@@ -703,7 +704,8 @@ open class CollectionViewChatLayout: NSUICollectionViewLayout {

// Checking `cachedCollectionViewWidth != collectionView.bounds.size.width` is necessary
// because the collection view's width can change without a `contentSizeAdjustment` occurring.
if context.contentSizeAdjustment.width != 0 || cachedCollectionViewSize != collectionView.bounds.size {

if context.contentSizeAdjustment.width != 0 || cachedCollectionViewSize != collectionView.scrollViewBounds.size {
prepareActions.formUnion([.cachePreviousWidth])
}

@@ -721,13 +723,14 @@ open class CollectionViewChatLayout: NSUICollectionViewLayout {
contentHeight != 0,
contentHeight > visibleBounds.size.height {
let adjustedContentInset: NSUIEdgeInsets = collectionView.adjustedContentInset
let maxAllowed = max(-adjustedContentInset.top, contentHeight - collectionView.frame.height + adjustedContentInset.bottom)
let maxAllowed = max(-adjustedContentInset.top, contentHeight - collectionView.scrollViewFrame.height + adjustedContentInset.bottom)
switch currentPositionSnapshot.edge {
case .top:
let desiredOffset = max(min(maxAllowed, frame.minY - currentPositionSnapshot.offset - adjustedContentInset.top - settings.additionalInsets.top), -adjustedContentInset.top)
context.contentOffsetAdjustment.y = desiredOffset - collectionView.contentOffset.y
case .bottom:
let desiredOffset = max(min(maxAllowed, frame.maxY + currentPositionSnapshot.offset - collectionView.bounds.height + adjustedContentInset.bottom + settings.additionalInsets.bottom), -adjustedContentInset.top)

let desiredOffset = max(min(maxAllowed, frame.maxY + currentPositionSnapshot.offset - collectionView.scrollViewBounds.height + adjustedContentInset.bottom + settings.additionalInsets.bottom), -adjustedContentInset.top)
context.contentOffsetAdjustment.y = desiredOffset - collectionView.contentOffset.y
}
}
@@ -773,7 +776,6 @@ open class CollectionViewChatLayout: NSUICollectionViewLayout {
let collectionView {
reconfigureItemsIndexPaths
.filter { collectionView.platformIndexPathsForVisibleItems.contains($0) && !controller.reloadedIndexes.contains($0) }

.forEach { indexPath in

#if canImport(AppKit) && !targetEnvironment(macCatalyst)
@@ -1129,7 +1131,8 @@ extension CollectionViewChatLayout {
guard let collectionView else {
return .zero
}
let maxContentOffset = max(0 - collectionView.adjustedContentInset.top, controller.contentHeight(at: state) - collectionView.frame.height + collectionView.adjustedContentInset.bottom)

let maxContentOffset = max(0 - collectionView.adjustedContentInset.top, controller.contentHeight(at: state) - collectionView.scrollViewFrame.height + collectionView.adjustedContentInset.bottom)
return CGPoint(x: 0, y: maxContentOffset)
}

28 changes: 22 additions & 6 deletions ChatLayout/Classes/Core/Extensions/CollectionView+Extension.swift
Original file line number Diff line number Diff line change
@@ -6,12 +6,10 @@ import AppKit
extension NSCollectionView {
var contentOffset: CGPoint {
set {
guard let scrollView = enclosingScrollView else { return }
scrollView.documentView?.scroll(newValue)
scroll(newValue)
}
get {
guard let scrollView = enclosingScrollView else { return .zero }
return scrollView.documentVisibleRect.origin
visibleRect.origin
}
}

@@ -47,8 +45,6 @@ extension NSCollectionView {
@objc private func didEndLiveScroll() {
isLiveScrolling = false
}


}
#endif

@@ -62,4 +58,24 @@ extension NSUICollectionView {
return indexPathsForVisibleItems
#endif
}

var scrollViewFrame: CGRect {
#if canImport(AppKit) && !targetEnvironment(macCatalyst)
return CGRect(x: frame.minX, y: frame.minY, width: visibleRect.width, height: visibleRect.height)
#endif

#if canImport(UIKit)
return frame
#endif
}

var scrollViewBounds: CGRect {
#if canImport(AppKit) && !targetEnvironment(macCatalyst)
return visibleRect
#endif

#if canImport(UIKit)
return bounds
#endif
}
}
Original file line number Diff line number Diff line change
@@ -21,7 +21,7 @@ public final class ContainerCollectionViewItem<CustomView: NSView>: NSCollection
}

/// Contained view.
public lazy var customView = CustomView(frame: view.bounds)
public lazy var customView = CustomView()

/// An instance of `ContainerCollectionViewCellDelegate`
public weak var delegate: ContainerCollectionViewCellDelegate?
22 changes: 14 additions & 8 deletions Example/ChatLayout.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
@@ -55,7 +55,7 @@
4504F40624FA66F600385590 /* CacheError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4504F3C624FA66F500385590 /* CacheError.swift */; };
4504F40724FA66F600385590 /* TextGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4504F3C724FA66F500385590 /* TextGenerator.swift */; };
4504F40824FA66F600385590 /* Caches.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4504F3C824FA66F500385590 /* Caches.swift */; };
4504F40924FA66F600385590 /* ChatViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4504F3CA24FA66F500385590 /* ChatViewController.swift */; };
4504F40924FA66F600385590 /* ChatViewController+UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4504F3CA24FA66F500385590 /* ChatViewController+UIKit.swift */; };
4504F40A24FA66F600385590 /* StatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4504F3CC24FA66F500385590 /* StatusView.swift */; };
4504F40B24FA66F600385590 /* ChatCollectionDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4504F3CE24FA66F500385590 /* ChatCollectionDataSource.swift */; };
4504F40C24FA66F600385590 /* DefaultChatCollectionDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4504F3CF24FA66F500385590 /* DefaultChatCollectionDataSource.swift */; };
@@ -86,9 +86,11 @@
65D4D44932474631D9677C50 /* Pods_ChatLayout_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 12FB56A5FF321AA24C456635 /* Pods_ChatLayout_Tests.framework */; };
BC98052E277D404F2FBB169E /* Pods_ChatLayout_Example_macOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 335A0DC42BFC13CD84BB1F50 /* Pods_ChatLayout_Example_macOS.framework */; };
D5A31E60E9BF6F8C13EDFDC5 /* PerformanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A31C93E56F2602C9BA605B /* PerformanceTests.swift */; };
E92946242CA3F09B00317763 /* ChatViewControllerBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BE54241EB5DD7BC464607EE /* ChatViewControllerBuilder.swift */; };
E92946252CA3F0DB00317763 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4504F42F24FA685E00385590 /* Assets.xcassets */; };
E92946282CA3FDFA00317763 /* ChatViewController+AppKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = E92946262CA3FDFA00317763 /* ChatViewController+AppKit.swift */; };
E9622F342CA2B6DE00EEA1CA /* Typealias.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9622F332CA2B6DE00EEA1CA /* Typealias.swift */; };
E9A283412CA2EB74007C8383 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9A283402CA2EB74007C8383 /* AppDelegate.swift */; };
E9A283432CA2EB75007C8383 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E9A283422CA2EB75007C8383 /* Assets.xcassets */; };
E9A283462CA2EB75007C8383 /* Base in Resources */ = {isa = PBXBuildFile; fileRef = E9A283452CA2EB75007C8383 /* Base */; };
E9A2834B2CA2EB90007C8383 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4504F3AB24FA66F500385590 /* Constants.swift */; };
E9A2834C2CA2EBA4007C8383 /* RawMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4504F3B824FA66F500385590 /* RawMessage.swift */; };
@@ -155,7 +157,7 @@
E9A2838C2CA2F247007C8383 /* URLController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4504F3E024FA66F500385590 /* URLController.swift */; };
E9A2838D2CA2F247007C8383 /* TextMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4504F3D224FA66F500385590 /* TextMessageView.swift */; };
E9A283922CA32133007C8383 /* NSLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9A2836D2CA2EFDC007C8383 /* NSLabel.swift */; };
E9A283932CA3283F007C8383 /* ChatViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4504F3CA24FA66F500385590 /* ChatViewController.swift */; };
E9A283932CA3283F007C8383 /* ChatViewController+UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4504F3CA24FA66F500385590 /* ChatViewController+UIKit.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
@@ -220,7 +222,7 @@
4504F3C624FA66F500385590 /* CacheError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CacheError.swift; sourceTree = "<group>"; };
4504F3C724FA66F500385590 /* TextGenerator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextGenerator.swift; sourceTree = "<group>"; };
4504F3C824FA66F500385590 /* Caches.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Caches.swift; sourceTree = "<group>"; };
4504F3CA24FA66F500385590 /* ChatViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatViewController.swift; sourceTree = "<group>"; };
4504F3CA24FA66F500385590 /* ChatViewController+UIKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ChatViewController+UIKit.swift"; sourceTree = "<group>"; };
4504F3CC24FA66F500385590 /* StatusView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusView.swift; sourceTree = "<group>"; };
4504F3CE24FA66F500385590 /* ChatCollectionDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatCollectionDataSource.swift; sourceTree = "<group>"; };
4504F3CF24FA66F500385590 /* DefaultChatCollectionDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefaultChatCollectionDataSource.swift; sourceTree = "<group>"; };
@@ -262,6 +264,7 @@
BDD575DC427C1961B83F685C /* Pods-ChatLayout_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ChatLayout_Example.release.xcconfig"; path = "Target Support Files/Pods-ChatLayout_Example/Pods-ChatLayout_Example.release.xcconfig"; sourceTree = "<group>"; };
CFE0D61EDC4670E905EC3EF0 /* Pods-ChatLayout_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ChatLayout_Example.debug.xcconfig"; path = "Target Support Files/Pods-ChatLayout_Example/Pods-ChatLayout_Example.debug.xcconfig"; sourceTree = "<group>"; };
D5A31C93E56F2602C9BA605B /* PerformanceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PerformanceTests.swift; sourceTree = "<group>"; };
E92946262CA3FDFA00317763 /* ChatViewController+AppKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ChatViewController+AppKit.swift"; sourceTree = "<group>"; };
E9622F332CA2B6DE00EEA1CA /* Typealias.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Typealias.swift; sourceTree = "<group>"; };
E9A2833E2CA2EB74007C8383 /* ChatLayout_Example_macOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ChatLayout_Example_macOS.app; sourceTree = BUILT_PRODUCTS_DIR; };
E9A283402CA2EB74007C8383 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
@@ -455,7 +458,8 @@
4504F3C924FA66F500385590 /* View */ = {
isa = PBXGroup;
children = (
4504F3CA24FA66F500385590 /* ChatViewController.swift */,
E92946262CA3FDFA00317763 /* ChatViewController+AppKit.swift */,
4504F3CA24FA66F500385590 /* ChatViewController+UIKit.swift */,
4504F3CB24FA66F500385590 /* Status View */,
4504F3CD24FA66F500385590 /* Data Source */,
4504F3D024FA66F500385590 /* Text Message View */,
@@ -785,7 +789,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
E9A283432CA2EB75007C8383 /* Assets.xcassets in Resources */,
E92946252CA3F0DB00317763 /* Assets.xcassets in Resources */,
E9A283462CA2EB75007C8383 /* Base in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -949,7 +953,7 @@
4504F41624FA66F600385590 /* AvatarView.swift in Sources */,
4504F40B24FA66F600385590 /* ChatCollectionDataSource.swift in Sources */,
4504F3FA24FA66F600385590 /* Message.swift in Sources */,
4504F40924FA66F600385590 /* ChatViewController.swift in Sources */,
4504F40924FA66F600385590 /* ChatViewController+UIKit.swift in Sources */,
4504F3F724FA66F600385590 /* DefaultRandomDataProvider.swift in Sources */,
4504F41424FA66F600385590 /* EditNotifierDelegate.swift in Sources */,
E9A283732CA2F0BC007C8383 /* AppKit+.swift in Sources */,
@@ -1031,6 +1035,7 @@
E9A2835D2CA2EBA4007C8383 /* IterativeCache.swift in Sources */,
E9A2835E2CA2EBA4007C8383 /* KeyValueCaching.swift in Sources */,
E9A2835F2CA2EBA4007C8383 /* DefaultChatController.swift in Sources */,
E92946242CA3F09B00317763 /* ChatViewControllerBuilder.swift in Sources */,
E9A283792CA2F236007C8383 /* URLView.swift in Sources */,
E9A2837A2CA2F247007C8383 /* AvatarPlaceholderView.swift in Sources */,
E9A2837B2CA2F247007C8383 /* BubbleController.swift in Sources */,
@@ -1046,9 +1051,10 @@
E9A283852CA2F247007C8383 /* BezierMaskedView.swift in Sources */,
E9A283862CA2F247007C8383 /* ManualAnimator.swift in Sources */,
E9A283872CA2F247007C8383 /* TextBubbleController.swift in Sources */,
E9A283932CA3283F007C8383 /* ChatViewController.swift in Sources */,
E9A283932CA3283F007C8383 /* ChatViewController+UIKit.swift in Sources */,
E9A283882CA2F247007C8383 /* DefaultChatCollectionDataSource.swift in Sources */,
E9A283892CA2F247007C8383 /* MainContainerView.swift in Sources */,
E92946282CA3FDFA00317763 /* ChatViewController+AppKit.swift in Sources */,
E9A2838A2CA2F247007C8383 /* ChatCollectionDataSource.swift in Sources */,
E9A2838B2CA2F247007C8383 /* EditNotifier.swift in Sources */,
E9A2838C2CA2F247007C8383 /* URLController.swift in Sources */,
Loading