Can NSCollectionView autoresize the width of its subviews to display one column
I have an NSCollectionView
that contains a collection of CustomViews
. Initially it tiled the subviews into columns and rows like a grid. I then set the Columns
property in IB to 1, so now it just displays them one after another in rows. However, even though my CustomView
is 400px wide, it's set to autoresize, the NSCol开发者_如何学JAVAlectionView
is 400px wide, and it's set to 1 column, the subviews are drawn about 80px wide.
I know I can get around this by calling:
CGFloat width = collectionView.bounds.size.width;
NSSize size = NSMakeSize(width, 85);
[collectionView setMinItemSize:size];
[collectionView setMaxItemSize:size];
But putting this code in the awakeFromNib
method of my WindowController
only sets the correct width when the program launches. When I resize the window (and the NSCollectionView
autoresizes as I've specified), the CustomViews stay at their initially set width.
I'm happy to take care of resizing the subviews myself if need be, but I'm quite new to Cocoa and can't seem to find any articles explaining how to do such a thing. Can someone point me in the right direction?
Anthony
The true answer is to set the maxItemSize to 0,0(NSZeroSize
). Otherwise, it is computed.
[self.collectionView setMaxItemSize:NSZeroSize];
This can be set in awakeFromNib
.
I couldn't get this to work with a default layout - but it is fairly easy to implement a custom layout:
/// Simple single column layout, assuming only one section
class SingleColumnLayout: NSCollectionViewLayout {
/// Height of each view in the collection
var height:CGFloat = 100
/// Padding is wrapped round each item, with double an extra bottom padding above the top item, and an extra top padding beneath the bottom
var padding = EdgeInsets.init(top: 5, left: 10, bottom: 5, right: 10)
var itemCount:Int {
guard let collectionView = collectionView else {
return 0
}
return collectionView.numberOfItems(inSection:0)
}
override func shouldInvalidateLayout(forBoundsChange newBounds: NSRect) -> Bool {
return true
}
override open func layoutAttributesForItem(at indexPath: IndexPath) -> NSCollectionViewLayoutAttributes? {
let attributes = NSCollectionViewLayoutAttributes(forItemWith: indexPath)
guard let collectionView = collectionView else {
return attributes
}
let bounds = collectionView.bounds
let itemHeightWithPadding = height + padding.top + padding.bottom
let row = indexPath.item
attributes.frame = NSRect(x: padding.left, y: itemHeightWithPadding * CGFloat(row) + padding.top + padding.bottom , width: bounds.width - padding.left - padding.right , height: height)
attributes.zIndex = row
return attributes
}
//If you have lots of items, then you should probably do a 'proper' implementation here
override open func layoutAttributesForElements(in rect: NSRect) -> [NSCollectionViewLayoutAttributes] {
var attributes = [NSCollectionViewLayoutAttributes]()
if (itemCount>0){
for index in 0...(itemCount-1) {
if let attribute = layoutAttributesForItem(at: NSIndexPath(forItem: index, inSection: 0) as IndexPath) {
attributes.append(attribute)
}
}
}
return attributes
}
override open var collectionViewContentSize: NSSize {
guard let collectionView = collectionView else {
return NSSize.zero
}
let itemHeightWithPadding = height + padding.top + padding.bottom
return NSSize.init(width: collectionView.bounds.width, height: CGFloat(itemCount) * itemHeightWithPadding + padding.top + padding.bottom )
}
}
then all you need is
var layout=SingleColumnLayout()
collectionView.collectionViewLayout = layout
I know this is a very late response but I got the same problem and hope my solution will help somebody. Solution is to access bounds of enclosing scroll view not of collection view itself. So to solve it you need to replace first line with:
CGFloat width = collectionView.enclosingScrollView.bounds.size.width;
another late one - I just switched to using an NSTableView
and providing an NSView
by the delegate
method.
Autoresizing comes for free, one column is easy, and it renders massively faster.
Lets say you want your CollectionViewItem with a size of 200x180px, then you should set:
[myCollectionView setMinItemSize:NSMakeSize(200, 180)];
[myCollectionView setMaxItemSize:NSMakeSize(280, 250)];
Your Max-Size should be big enough to look good and give enough space for stretching to fit the collectionView-Width.
If you have a fixed number of columns, you can probably use (0,0), but if you want a dynamic number of rows and columns like I wanted.. you should set a fixed min-size and a bigger max.size.
While you might get a collection view to behave as you want, I think you have a design problem
You should use a NSTableView
and set columns to 1 and their sizing to anything but "None". NSTableView
is intended for tabular data, plus it can recycle cells which gives a great performance boost for large amount of items. NSCollectionView
is more like a linear layout which arranges items in a grid, with vertical scrolling. It is useful when the column number can change dynamically to allow more items to be shown on screen, usually depending on device orientation and screen size.
I tried all of solutions proposed here and none of them helped. If you use flow layout (it's used by default) you can extend it with the following code and delegate's sizeForItem method will be called on each change
class MyCollectionViewFlowLayout: NSCollectionViewFlowLayout {
override func shouldInvalidateLayout(forBoundsChange newBounds: NSRect) -> Bool {
return true
}
override func invalidationContext(forBoundsChange newBounds: NSRect) -> NSCollectionViewLayoutInvalidationContext {
let context = super.invalidationContext(forBoundsChange: newBounds) as! NSCollectionViewFlowLayoutInvalidationContext
context.invalidateFlowLayoutDelegateMetrics = true
return context
}
}
Hope it helps someone as it took me couple of evenings to find solution
Matt Gallagher's wonderful blog Cocoa with Love is about to address this. This week, he shared the bindings details of a one-column view like the one in question:
http://cocoawithlove.com/2010/03/designing-view-with-bindings.html
Next entry, he promises to share the rest of the implementation, which should give you what you're looking for.
(I should note that he is subclassing NSView directly and re-implementing many of NSCollectionView's features. This seems to be common though; not many people are fond of subclassing NSCollectionView.)
Edit: Seems he broke his promise... we never did receive that post. See tofutim's answer below instead.
精彩评论