Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 NSCollectionView 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

like image 992
littlecharva Avatar asked Jan 21 '10 08:01

littlecharva


3 Answers

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.

like image 194
tofutim Avatar answered Sep 19 '22 00:09

tofutim


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;
like image 42
Nikita Avatar answered Sep 19 '22 00:09

Nikita


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
like image 32
Confused Vorlon Avatar answered Sep 20 '22 00:09

Confused Vorlon