Reputation: 762
I'm beginning to suspect NSCollectionView + Compositional Layout on macOS is very buggy ... 😅
I'm using NSCollectionView + Compositional layout.
My layout is toggle-able between an orthogonal scrolling section or a more standard 'tiled' section.
Behavior I am noticing when I am the orthogonal scrolling mode is my section headers have NSLayoutConstraints automatically set by the compositional layout engine that have the wrong origin.
In this screenshot, I am in orthogonal scrolling mode.
You can see that sections which have a small number of items have the section header to beyond the leading edge of the scroll view. What is interesting, is origin adapts to the width of the scroll view.
The wider I made the view, the larger the offset to the left is:
Here is a non orthogonal scrolling screenshot which has expected header layouts with the right origin:
My Header view has translatesAutoresizingMaskIntoConstraints
set to false, and uses constraints within it (and those constraints are correct, its just the origin of the scroll view which is off).
I've read other issues on here quirks of compositional layout
Im running Xcode 15.1 on macOS 13.6.3 if that makes a difference,
Any insights or suggestions welcome.
My layout is as follows:
func createLayout()
{
let config = NSCollectionViewCompositionalLayoutConfiguration()
config.scrollDirection = .vertical
config.interSectionSpacing = 16.0
let layout = NSCollectionViewCompositionalLayout(sectionProvider: {
(sectionIndex: Int, layoutEnvironment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection? in
let snapshot = self.dataSource.snapshot()
let width = layoutEnvironment.container.effectiveContentSize.width
if self.layoutWrap
{
return self.mediaLayoutSection(snapshot:snapshot, forEnvironmentWidth: width)
}
else
{
return self.mediaLayoutSection(snapshot:snapshot, forEnvironmentHeight:150)
}
}, configuration: config)
layout.register(OzuBackgroundRoundedView.self, forDecorationViewOfKind: OzuBackgroundRoundedView.kind)
self.collectionView.collectionViewLayout = layout
}
private func mediaLayoutSection(snapshot:NSDiffableDataSourceSnapshot<VideoAssetWrapper, SegmentEditViewWrapper>, forEnvironmentHeight:CGFloat) -> NSCollectionLayoutSection
{
let itemHeight = floor( forEnvironmentHeight )
let aspect = 16.0 / 9.0
let topPadding = 6.0
let bottomPadding = 6.0
let leftPadding = 6.0
let rightPadding = 6.0
let widthDimension:NSCollectionLayoutDimension = .absolute( (itemHeight * aspect) )
let heightDimension:NSCollectionLayoutDimension = .absolute( itemHeight )
let itemSize = NSCollectionLayoutSize(widthDimension: widthDimension,
heightDimension: heightDimension )
let item = NSCollectionLayoutItem(layoutSize: itemSize)
item.contentInsets = .init(top: topPadding, leading: leftPadding, bottom: bottomPadding, trailing: rightPadding)
let groupSize = NSCollectionLayoutSize(widthDimension: widthDimension,
heightDimension: heightDimension )
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
let section = NSCollectionLayoutSection(group: group)
section.orthogonalScrollingBehavior = .continuous
section.contentInsets = NSDirectionalEdgeInsets(top: 6, leading: 6, bottom: 6, trailing:6)
section.boundarySupplementaryItems = [self.makeSectionHeader()]
section.decorationItems = [ self.makeBackgroundItem() ]
section.supplementariesFollowContentInsets = true
return section
}
func mediaLayoutSection(snapshot:NSDiffableDataSourceSnapshot<VideoAssetWrapper, SegmentEditViewWrapper>, forEnvironmentWidth:CGFloat) -> NSCollectionLayoutSection
{
let columnCount = Int( self.layoutSize )
let itemWidth = floor( forEnvironmentWidth / CGFloat( columnCount ) )
let aspect = 9.0 / 16.0
let topPadding = 6.0
let bottomPadding = 6.0
let leftPadding = 6.0
let rightPadding = 6.0
let itemSize = NSCollectionLayoutSize(widthDimension: .absolute( itemWidth ),
heightDimension: .absolute( itemWidth * aspect ))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
item.contentInsets = .init(top: topPadding, leading: leftPadding, bottom: bottomPadding, trailing: rightPadding)
let groupSize = NSCollectionLayoutSize(widthDimension: .absolute( forEnvironmentWidth ),
heightDimension: .absolute( itemWidth * aspect ) )
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
let section = NSCollectionLayoutSection(group: group)
section.contentInsets = NSDirectionalEdgeInsets(top: 6, leading: 6, bottom: 6, trailing:6)
section.boundarySupplementaryItems = [self.makeSectionHeader()]
section.decorationItems = [ self.makeBackgroundItem() ]
section.supplementariesFollowContentInsets = true
return section
}
private func makeSectionHeader() -> NSCollectionLayoutBoundarySupplementaryItem
{
let layoutSectionHeaderItemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(50))
let layoutSectionHeaderItem = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: layoutSectionHeaderItemSize, elementKind: VideoAssetHeaderView.kind, alignment: .top)
layoutSectionHeaderItem.pinToVisibleBounds = true
return layoutSectionHeaderItem
}
private func makeBackgroundItem() -> NSCollectionLayoutDecorationItem
{
let background = NSCollectionLayoutDecorationItem.background(elementKind: OzuBackgroundRoundedView.kind)
background.contentInsets = NSDirectionalEdgeInsets(top: 6, leading: 6, bottom: 6, trailing:6)
return background
}
Upvotes: 0
Views: 127
Reputation: 762
So, interestingly, this seems to be some weird behavior with pinToVisibleBounds
with orthogonal scrolling.
Disabling pinToVisibleBounds
fixes some of the odd behaviors I see.
Upvotes: 0