NSCollectionView as NSCollectionViewItem

348 Views Asked by At

Here is the window I want to achieve: window to achieve It is a NSViewController (RedViewController) with NSCollectionView (RedCollectionView) as view.
It item (BlueItem) has a NSCollectionView (BlueCollectionView) as view.
It item's item (GreenItem), has a NSImageView (GreenImageView) as view.
The RedCollectionView will scroll vertically, and its items will be selectable. However the BlueCollectionView is just for display, it won't scroll itself and won't be selectable.
Here is the code for the AppDelegate:

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
  lazy var redViewController: RedViewController = {
    let redViewController = RedViewController.init()
    return redViewController
  }()

  lazy var window: NSWindow = {
    let window = NSWindow.init(contentViewController: self.redViewController)
    return window
  }()

  lazy var windowController: NSWindowController = {
    let windowController = NSWindowController.init(window: self.window)
    return windowController
  }()

  func applicationDidFinishLaunching(_ aNotification: Notification) {
    self.windowController.showWindow(nil)
  }
}

Here is the code for the RedViewController:

class RedViewController: NSViewController, NSCollectionViewDataSource, NSCollectionViewDelegate {
  lazy var collectionViewLayout: NSCollectionViewFlowLayout = {
    let collectionViewLayout = NSCollectionViewFlowLayout.init()
    collectionViewLayout.itemSize = NSSize(width: 100.0, height: 100.0)
    collectionViewLayout.minimumLineSpacing = 10.0
    collectionViewLayout.minimumInteritemSpacing = 10.0
    collectionViewLayout.sectionInset = NSEdgeInsetsMake(10.0, 10.0, 10.0, 10.0)
    return collectionViewLayout
  }()

  class RedCollectionView: NSCollectionView { /* nothing here for now */ }

  lazy var redCollectionView: RedCollectionView = {
    let redCollectionView = RedCollectionView.init(frame: NSRect(x: 0.0, y: 0.0, width: 500.0, height: 500.0))
    redCollectionView.collectionViewLayout = self.collectionViewLayout
    redCollectionView.dataSource = self
    redCollectionView.delegate = self
    redCollectionView.isSelectable = true
    redCollectionView.backgroundColors = [NSColor.red]
    return redCollectionView
  }()

  override func loadView() {
    let clipView = NSClipView.init()
    clipView.documentView = self.redCollectionView
    let scrollView = NSScrollView.init(frame: NSRect(x: 0.0, y: 0.0, width: 500.0, height: 500.0))
    scrollView.contentView = clipView
    self.view = scrollView
  }

  override func viewDidLoad() {
    super.viewDidLoad()
    self.redCollectionView.register(BlueItem.self, forItemWithIdentifier: NSUserInterfaceItemIdentifier("BlueItemID"))
  }

  func numberOfSections(in collectionView: NSCollectionView) -> Int {
    return 1
  }

  func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
    return 50
  }

  func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
    let item = collectionView.makeItem(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "BlueItemID"), for: indexPath)
    guard let blueItem = item as? BlueItem else { return item }
    return blueItem
  }

  func collectionView(_ collectionView: NSCollectionView, didSelectItemsAt indexPaths: Set<IndexPath>) {
    indexPaths.forEach({ print($0) })
    collectionView.deselectItems(at: indexPaths)
  }
}

Here is the code for the BlueItem:

class BlueItem: NSCollectionViewItem, NSCollectionViewDataSource {
  lazy var collectionViewLayout: NSCollectionViewFlowLayout = {
    let collectionViewLayout = NSCollectionViewFlowLayout.init()
    collectionViewLayout.itemSize = NSSize(width: 27.0, height: 27.0)
    collectionViewLayout.minimumLineSpacing = 3.0
    collectionViewLayout.minimumInteritemSpacing = 3.0
    collectionViewLayout.sectionInset = NSEdgeInsetsMake(3.0, 3.0, 3.0, 3.0)
    return collectionViewLayout
  }()

  class BlueCollectionView: NSCollectionView {
    override func mouseDown(with event: NSEvent) {
      super.mouseDown(with: event)
      self.nextResponder?.mouseDown(with: event)
    }

    override func mouseUp(with event: NSEvent) {
      super.mouseUp(with: event)
      self.nextResponder?.mouseUp(with: event)
    }
  }

  lazy var blueCollectionView: BlueCollectionView = {
    let blueCollectionView = BlueCollectionView.init(frame: NSRect(x: 0.0, y: 0.0, width: 100.0, height: 100.0))
    blueCollectionView.collectionViewLayout = self.collectionViewLayout
    blueCollectionView.dataSource = self
    blueCollectionView.isSelectable = false
    blueCollectionView.backgroundColors = [NSColor.blue]
    return blueCollectionView
  }()

  override func loadView() {
    self.view = self.blueCollectionView
  }

  override func viewDidLoad() {
    super.viewDidLoad()
    self.blueCollectionView.register(GreenItem.self, forItemWithIdentifier: NSUserInterfaceItemIdentifier("GreenItemID"))
  }

  func numberOfSections(in collectionView: NSCollectionView) -> Int {
    return 1
  }

  func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
    return 7
  }

  func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
    let item = collectionView.makeItem(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "GreenItemID"), for: indexPath)
    guard let greenItem = item as? GreenItem else { return item }
    return greenItem
  }
}

And finally here is the code for the GreenItem:

class GreenItem: NSCollectionViewItem {
  class GreenImageView: NSImageView { /* nothing here for now */ }

  lazy var greenImageView: GreenImageView = {
    let image: NSImage = NSImage.init(imageLiteralResourceName: "greenImage")
    let greenImageView = GreenImageView.init(image: image)
    return greenImageView
  }()

  override func loadView() {
    self.view = self.greenImageView
  }
}

When I launch the app, I get this result: app screenshot

So I have 3 issues:
1. The BlueItem with it BlueCollectionView don't get the blue background expected.
2. The BlueCollectionView flow layout tries to display all the items on the first row. It looks like it doesn't know the collection view size.
3. When I try to select a BlueItem: if I click on an empty space inside the BlueItem, the RedCollectionView delegate is triggered as expected, but if I click on a green part inside the BlueItem, the RedCollectionView delegate isn't triggered.

0

There are 0 best solutions below