12 Replies
      Latest reply on Jul 18, 2019 12:28 PM by Claude31
      nouatzi Level 1 Level 1 (0 points)

        I have a NSCollectionView (let's call it parent) where each item is a NSCollectionView (call it child). And the child's item is just an NSImageView. In other word, it's a collection of image collection. The child collection is just for display, I don't need scroll nor selection.

        However selection for the parent collection doesn't work. Its func collectionView(_ collectionView: NSCollectionView, didSelectItemsAt indexPaths: Set) doesn't get called.

        Normal selection works because when I replace its item with a simple NSView, it get called.

        What could cause this issue ?

        Here is the code for the parent collection view controller:

        class ParentViewController: NSViewController, NSCollectionViewDataSource, NSCollectionViewDelegate {
          lazy var collectionViewLayout: NSCollectionViewFlowLayout = {
          let collectionViewLayout: NSCollectionViewFlowLayout = 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
          }()
        
          lazy var collectionView: NSCollectionView = {
          let collectionView: NSCollectionView = NSCollectionView.init()
          collectionView.collectionViewLayout = self.collectionViewLayout
          collectionView.dataSource = self
          collectionView.delegate = self
          collectionView.isSelectable = true
          collectionView.backgroundColors = [NSColor.red, NSColor.red]
          return collectionView
          }()
        
          override func loadView() {
          let clipView: NSClipView = NSClipView.init()
          clipView.documentView = self.collectionView
          let scrollView: NSScrollView = 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.collectionView.register(ParentItem.self, forItemWithIdentifier: NSUserInterfaceItemIdentifier("ParentItemID"))
          }
        
          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: NSCollectionViewItem = collectionView.makeItem(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "ParentItemID"), for: indexPath)
          guard let parentItem: ParentItem = item as? ParentItem else { return item }
          return parentItem
          }
        
          func collectionView(_ collectionView: NSCollectionView, didSelectItemsAt indexPaths: Set<IndexPath>) {
          indexPaths.forEach({ print($0) })
          collectionView.deselectItems(at: indexPaths)
          }
        }

        Here is the child collection view controller, which is the parent item:

        class ParentItem: NSCollectionViewItem, NSCollectionViewDataSource {
          lazy var collectionViewLayout: NSCollectionViewFlowLayout = {
            let collectionViewLayout: NSCollectionViewFlowLayout = NSCollectionViewFlowLayout.init()
            collectionViewLayout.estimatedItemSize = NSZeroSize
            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 ChildCollectionView: NSCollectionView {
            // nothing for now
          }
        
        
          lazy var mycollectionView: ChildCollectionView = {
            let mycollectionView: ChildCollectionView = ChildCollectionView.init(frame: NSRect(x: 0.0, y: 0.0, width: 100.0, height: 100.0))
            mycollectionView.collectionViewLayout = self.collectionViewLayout
            mycollectionView.dataSource = self
            mycollectionView.backgroundColors = [NSColor.blue, NSColor.blue]
            return mycollectionView
          }()
        
        
          override func loadView() {
            self.view = mycollectionView
          }
        
        
          override func viewDidLoad() {
            super.viewDidLoad()
            self.mycollectionView.register(ChildItem.self, forItemWithIdentifier: NSUserInterfaceItemIdentifier("ChildItemID"))
          }
        
        
          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: NSCollectionViewItem = collectionView.makeItem(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "ChildItemID"), for: indexPath)
            guard let childItem: ChildItem = item as? ChildItem else { return item }
            return childItem
          }
        }
        

        And finally the child item:

        class ChildItem: NSCollectionViewItem {
          lazy var faviconImageView: ImageView = {
          let image: NSImage = NSImage.init(imageLiteralResourceName: "anImage")
          let faviconImageView: ImageView = ImageView.init(image: image)
          return faviconImageView
          }()
        
          override func loadView() {
          self.view = self.faviconImageView
          }
        }
        • Re: collection view as collection item (nested collection views)
          Claude31 Level 8 Level 8 (6,155 points)

          Could you add

           

            func collectionView(_ collectionView: NSCollectionView, didSelectItemsAt indexPaths: Set) {
            indexPaths.forEach({ print($0) })
            }

           

          in ParentItem

           

          and tell what you get.

            • Re: collection view as collection item (nested collection views)
              nouatzi Level 1 Level 1 (0 points)

              So I've finally found a solution.

              In the ParentItem:

                class ChildCollectionView: 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 mycollectionView: ChildCollectionView = {
                  let mycollectionView: ChildCollectionView = ChildCollectionView.init(frame: NSRect(x: 0.0, y: 0.0, width: 100.0, height: 100.0))
                  mycollectionView.collectionViewLayout = self.collectionViewLayout
                  mycollectionView.dataSource = self
                  mycollectionView.isSelectable = true
                  mycollectionView.backgroundColors = [NSColor.blue, NSColor.blue]
                  return mycollectionView
                }()

              In the ParentViewController:

                func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
                  let item: NSCollectionViewItem = collectionView.makeItem(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "ParentItemID"), for: indexPath)
                  guard let parentItem: ParentItem = item as? ParentItem else { return item }
                  if parentItem.mycollectionView.delegate !== self { parentItem.mycollectionView.delegate = self }
                  return parentItem
                }
                
                func collectionView(_ collectionView: NSCollectionView, didSelectItemsAt indexPaths: Set) {
                  if collectionView === self.collectionView {
                    indexPaths.forEach({ print($0) })
                  }
                  else {
                    let center: NSPoint = collectionView.center
                    if let indexPath = self.collectionView.indexPathForItem(at: center) {
                      print(indexPath)
                    }
                  }
                  collectionView.deselectItems(at: indexPaths)
                }

              And a small extension in order to get the center point:

              extension NSView {
                var center: NSPoint { return CGPoint(x: NSMidX(self.frame), y: NSMidY(self.frame)) }
              }

               

               

              BUT, I have an issue in the ParentItem, with the flow layout: It continues on a single row. It's like it doesn't know the collectionview size, in order to change rows.

              Now if I use NSCollectionViewGridLayout, it displays well, but selection doesn't work anymore.