9

I'm coming from the iOS development and I am wondering if there is a way to set up a NSCollectionView programmatically like a UICollectionView in iOS? And add the NSCollectionViewItems in code. Or is the only way to set up a NSCollectionView to use bindings?

Thank You!

Andriy
  • 2,767
  • 2
  • 21
  • 29
stefOCDP
  • 803
  • 2
  • 12
  • 20
  • To answer the last question, no, you do not need to use bindings. There is no view or control for which you need to use bindings. – stevesliva May 05 '15 at 20:33
  • But how can I do it without bindings? – stefOCDP May 05 '15 at 22:34
  • 1
    Convert [this](http://stackoverflow.com/questions/8660626/how-to-create-nscollectionview-programatically-from-scratch) to swift. – stevesliva May 05 '15 at 23:09
  • There are however situations in which you need to use bindings, namely when the class and/or value of an object is not yet known at design time. Bindings to representedObject, vital for NSCollectionViewItem instances, are a prime example. – Elise van Looij Mar 29 '17 at 14:09

3 Answers3

7

Thanks to @stevesliva for pointing me to this SO answer. I converted it to Swift. This is what I got.

I am creating a NSCollectionView in the ViewController:

import Cocoa

class ViewController: NSViewController {

var titles = [String]()
var collectionView: NSCollectionView?

override func viewDidLoad() {
    super.viewDidLoad()

    self.titles = ["Banana", "Apple", "Strawberry", "Cherry", "Pear", "Pineapple", "Grape", "Melon"]
    collectionView = NSCollectionView(frame: self.view.frame)
    collectionView!.itemPrototype = CollectionViewItem()
    collectionView!.content = self.titles
    collectionView!.autoresizingMask = NSAutoresizingMaskOptions.ViewWidthSizable | NSAutoresizingMaskOptions.ViewMaxXMargin | NSAutoresizingMaskOptions.ViewMinYMargin | NSAutoresizingMaskOptions.ViewHeightSizable | NSAutoresizingMaskOptions.ViewMaxYMargin

    var index = 0
    for title in titles {
        var item = self.collectionView!.itemAtIndex(index) as! CollectionViewItem
        item.getView().button?.title = self.titles[index]
        index++
    }
    self.view.addSubview(collectionView!)

}
}

The created CollectionViewItem in the ViewController just load a view, where I set up the item view itself.

import Cocoa

class CollectionViewItem: NSCollectionViewItem {

var itemView: ItemView?

override func viewDidLoad() {
    super.viewDidLoad()
    // Do view setup here.
}

override func loadView() {
    self.itemView = ItemView(frame: NSZeroRect)
    self.view = self.itemView!
}

func getView() -> ItemView {
    return self.itemView!
}
}

The view itself:

import Cocoa

class ItemView: NSView {

let buttonSize: NSSize = NSSize(width: 100, height: 20)
let itemSize: NSSize = NSSize(width: 120, height: 40)
let buttonOrigin: NSPoint = NSPoint(x: 10, y: 10)

var button: NSButton?

override func drawRect(dirtyRect: NSRect) {
    super.drawRect(dirtyRect)

    // Drawing code here.
}

override init(frame frameRect: NSRect) {
    super.init(frame: NSRect(origin: frameRect.origin, size: itemSize))
    let newButton = NSButton(frame: NSRect(origin: buttonOrigin, size: buttonSize))
    newButton.bezelStyle = NSBezelStyle.RoundedBezelStyle
    self.addSubview(newButton)
    self.button = newButton;
}

required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

func setButtonTitle(title: String) {
    self.button!.title = title
}
}

To set the button title, I am using kind of a hack. (the for-loop in the ViewController) If there is a better way to set the title please feel free to leave a comment.

Community
  • 1
  • 1
stefOCDP
  • 803
  • 2
  • 12
  • 20
  • To set the button title: In the for loop of the ViewController use: 'item.representedObject = title'. Then, in ItemView: 'NSButton *aButton = [[NSButton alloc] initWithFrame:NSMakeRect(10, 10, 100, 20)]; [aButton bind:NSTitleBinding toObject:self withKeyPath:@"representedObject" options:@{NSNullPlaceholderBindingOption: @"No title"}]; [self.view addSubview:aButton];' Don't use 'button.title = representedObject' since the representedObject won't be known at that time. The binding will resolve at runtime. – Elise van Looij Mar 29 '17 at 14:06
5

For Swift 2.0, you will need to change the collectionView!.autoresizingMask line to:

long hand:

collectionView?.autoresizingMask = NSAutoresizingMaskOptions([NSAutoresizingMaskOptions.ViewWidthSizable,NSAutoresizingMaskOptions.ViewMaxXMargin,NSAutoresizingMaskOptions.ViewMinYMargin,NSAutoresizingMaskOptions.ViewHeightSizable,NSAutoresizingMaskOptions.ViewMaxYMargin])

or short hand:

collectionView?.autoresizingMask = NSAutoresizingMaskOptions([.ViewWidthSizable,.ViewMaxXMargin,.ViewMinYMargin,.ViewHeightSizable,.ViewMaxYMargin])
Andriy
  • 2,767
  • 2
  • 21
  • 29
Swift Soda
  • 166
  • 2
  • 3
3

You need to embed NSCollectionView within NSScrollView. Code below is in Swift 4

Setup NSCollectionView

let layout = NSCollectionViewFlowLayout()
layout.minimumLineSpacing = 4

collectionView = NSCollectionView()
collectionView.dataSource = self
collectionView.delegate = self
collectionView.collectionViewLayout = layout
collectionView.allowsMultipleSelection = false
collectionView.backgroundColors = [.clear]
collectionView.isSelectable = true
collectionView.register(
  Cell.self,
  forItemWithIdentifier: NSUserInterfaceItemIdentifier(rawValue: "Cell")
)

Setup NSScrollView

scrollView = NSScrollView()
scrollView.documentView = collectionView
view.addSubview(scrollView)

Here's a simple NSCollectionViewItem

class Cell: NSCollectionViewItem {
  let label = NSTextField()
  let imageView = NSImageView()

  override func loadView() {
    self.view = NSView()
    self.view.wantsLayer = true
  }
}
onmyway133
  • 45,645
  • 31
  • 257
  • 263