NSCollectionView custom layout allows you to scroll

I cannot scroll both vertically and horizontally to work with custom layout for NSCollectionView. According to the docs, in my subclass, I return `collectionViewContentSize ', and if it is too large, the scroll is automatically included in the span scroll of the collection view. However, even if I order all items in a horizontal row, only vertical scrolling is enabled.

Here is a screenshot: http://i.stack.imgur.com/tMbCN.png

Here is my layout code:

import Cocoa class Layout: NSCollectionViewLayout { var cellSize = CGSize(width: 100, height: 30) var cellSpacing: CGFloat = 10 var sectionSpacing: CGFloat = 20 private var contentSize = CGSize.zero private var layoutAttributes = [NSIndexPath: NSCollectionViewLayoutAttributes]() override func prepareLayout() { guard let collectionView = collectionView else { return } let sections = collectionView.numberOfSections guard sections > 0 else { return } contentSize.height = cellSize.height for section in 0..<sections { let items = collectionView.numberOfItemsInSection(section) guard items > 0 else { break } for item in 0..<items { let origin = CGPoint(x: contentSize.width, y: 0) let indexPath = NSIndexPath(forItem: item, inSection: section) let attributes = NSCollectionViewLayoutAttributes(forItemWithIndexPath: indexPath) attributes.frame = CGRect(origin: origin, size: cellSize) layoutAttributes[indexPath] = attributes contentSize.width += cellSize.width + cellSpacing } contentSize.width += sectionSpacing } } override var collectionViewContentSize: NSSize { return contentSize } override func layoutAttributesForElementsInRect(rect: NSRect) -> [NSCollectionViewLayoutAttributes] { return layoutAttributes.values.filter { $0.frame.intersects(rect) } } override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> NSCollectionViewLayoutAttributes? { return layoutAttributes[indexPath] } override func shouldInvalidateLayoutForBoundsChange(newBounds: NSRect) -> Bool { return false } } 
+5
source share
4 answers

This error is still ongoing. I turned Bo Yuan into a nasty hack into Swift. (No offense, Bo Yuan, this is NOT YOUR mistake, we have to make this a terrible workaround! I am still breaking this code as soon as there is an official fix. For now, this is only going to save my development efforts.)

 class HackedCollectionView: NSCollectionView { override func setFrameSize(_ newSize: NSSize) { let size = collectionViewLayout?.collectionViewContentSize ?? newSize super.setFrameSize(size) if let scrollView = enclosingScrollView { scrollView.hasHorizontalScroller = size.width > scrollView.frame.width } } } 

Note that this definitely needs to be done as soon as possible, since it causes a call for each frame of the animation when scrolling. Not good. Please, please ask someone to solve this problem or find a suitable solution. It is impossible to scroll horizontally funny.

+4
source

This is a major mistake in NSCollectionView. As a workaround, you can implement the scrollDirection method (from NSCollectionViewFlowLayout) and return NSCollectionViewScrollDirectionHorizontal.

+1
source

I understood the solution. I created a subclass of NSCollectionView. And use this subclass to replace the standard NSCollectionView. I created a function to override setFrameSize. Voila!

 -(void)setFrameSize:(NSSize)newSize{ if (newSize.width != self.collectionViewLayout.collectionViewContentSize.width) { newSize.width = self.collectionViewLayout.collectionViewContentSize.width; } [super setFrameSize:newSize]; //NSLog(@"setFrameSize%@", CGSizeCreateDictionaryRepresentation(newSize)); } 

During my test, setFrameSize is called, and some mechanism first sets the frame size to {0,0}, and then sets it again so that the width is the same width as the clip and keeps the height from the size of the layout content.

0
source

It seems that only NSCollectionViewFlowLayout can dictate a frame that is wider than the parent scroll view frame.

The solution is to subclass NSCollectionViewFlowLayout instead of NSCollectionViewLayout. Treat the subclass as you would any other subclass of the layout, but add a critical scrollDirection to prepareLayout ().

Below is the minimum implementation for a layout that scrolls horizontally and simply sets all the elements next to each other.

 -(void) prepareLayout { [super prepareLayout]; self.scrollDirection = NSCollectionViewScrollDirectionHorizontal; } -(NSSize) collectionViewContentSize { NSSize itemSize = self.itemSize; NSInteger num = [self.collectionView numberOfItemsInSection:0]; NSSize contentSize = NSMakeSize((num * itemSize.width) + ((num+1) * self.minimumLineSpacing), NSHeight(self.collectionView.frame)); return contentSize; } -(BOOL) shouldInvalidateLayoutForBoundsChange:(NSRect)newBounds { return YES; } -(NSArray<__kindof NSCollectionViewLayoutAttributes *> *) layoutAttributesForElementsInRect:(NSRect)rect { int numItems = [self.collectionView numberOfItemsInSection:0]; NSMutableArray* attributes = [NSMutableArray arrayWithCapacity:numItems]; for (int i=0; i<numItems; i++) { [attributes addObject:[self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:i inSection:0]]]; } return attributes; } -(NSCollectionViewLayoutAttributes*) layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath { NSSize itemSize = self.itemSize; NSRect fr = NSZeroRect; fr.size = itemSize; fr.origin.y = (NSHeight(self.collectionView.frame) - itemSize.height) / 2.0; fr.origin.x = (indexPath.item+1 * self.minimumLineSpacing) + (indexPath.item * itemSize.width); NSCollectionViewLayoutAttributes* attr = [NSCollectionViewLayoutAttributes layoutAttributesForItemWithIndexPath:indexPath]; attr.frame = fr; return attr; } 
0
source

All Articles