Introducing AdvancedCollectionTableView, a Framework for NSCollectionView & NSTableView (ports many newer UIKit APIs)

I created an open source framework for NSCollectionView and NSTableView.

It's a collection of classes and extensions, many of them being ports of UIKit.

Github: AdvancedCollectionTableView
Documentation: Online

NSCollectionView ItemRegistration & NSTableView CellRegistration

A registration for collection view items and table cells that greatly simplifies configurating them. A port of UICollectionView.CellRegistration.

struct GalleryItem {
    let title: String
    let image: NSImage
}

let tableCellRegistration = NSTableView.CellRegistration<NSTableCellView, GalleryItem> { 
    tableCell, column, row, galleryItem in
    
    tableCell.textField.stringValue = galleryItem.title
    tableCell.imageView.image = galleryItem.image
    
    // Gets called whenever the state of the item changes (e.g. on selection)
    tableCell.configurationUpdateHandler = { tableCell, state in
        // Updates the text color based on selection state.
        tableCell.textField.textColor = state.isSelected ? .controlAccentColor : .labelColor
    }
}

NSContentConfiguration

Configurates styling and content for a content view. A port of UIContentConfiguration`.

NSCollectionviewItem, NSTableCellView and NSTableRowView provide contentConfiguration where you can apply them to configurate the content of the item/cell.

NSHostingConfiguration

A content configuration suitable for hosting a hierarchy of SwiftUI views.

With this configuration you can easily display a SwiftUI view in a collection item and table cell:

collectionViewItem.contentConfiguration = NSHostingConfiguration {
    HStack {
        Image(systemName: "star").foregroundStyle(.purple)
        Text("Favorites")
        Spacer()
    }
}

NSListContentConfiguration

A content configuration for a table view cell.

NSListContentConfiguration

var content = tableCell.defaultContentConfiguration()

// Configure content
content.text = "Text"
content.secondaryText = #"SecondaryText\\nImage displays a system image named "photo""#
content.image = NSImage(systemSymbolName: "photo")

// Customize appearance
content.textProperties.font = .body
content.imageProperties.tintColor = .controlAccentColor

tableCell.contentConfiguration = content

NSItemContentconfiguration

A content configuration for a collection view item.

NSItemContentconfiguration

public var content = collectionViewItem.defaultContentConfiguration()

// Configure content
content.text = "Text"
content.secondaryText = "SecondaryText"
content.image = NSImage(systemSymbolName: "Astronaut Cat")

// Customize appearance
content.secondaryTextProperties.font = .callout

collectionViewItem.contentConfiguration = content

NSCollectionView reconfigureItems

Updates the data for the items without reloading and replacing them. It provides much better performance compared to reloading items. A port of UICollectionView.reconfigureItems.

Any item that has been registered via ItemRegistration, or by class using register(_ itemClass: NSCollectionViewItem.Type), can be recofigurated.

collectionView.reconfigureItems(at: indexPaths)

NSTableView register NSTableCellView by Class

Apple only allows registering NSTableCellView using NSNib. This framework lets you register table cell class.

tableView.register(NSTableCellView.self)

let dequeuedTableCell = tableView.makeView(for: NSTableCellView.self)

NSCollectionView & NSTableViewDiffableDataSource allowsDeleting

allowsDeleting enables deleting of items and rows via backspace.

diffableDataSource.allowsDeleting = true

NSDiffableDataSourceSnapshot Apply Options

Apple's apply(_:animatingDifferences:completion:) provides two options for applying snapshots to a diffable data source depending on animatingDifferences:

  • true applies a diff of the old and new state and animates updates in the UI.
  • false is equivalent to calling reloadData(). It reloads every item.

NSDiffableDataSourceSnapshotApplyOption lets you perform a diff even without animations for much better performance compared to using Apple's reloadData().

It also provides additional options:

  • usingReloadData: All items get reloaded.
  • animated(withDuration: CGFloat): Changes get applied animated.
  • nonAnimated: Changes get applied immediatly.
diffableDataSource.apply(mySnapshot, .withoutAnimation)

diffableDataSource.apply(mySnapshot, .animated(3.0))

CollectionViewDiffableDataSource

An extended `NSCollectionViewDiffableDataSource that provides:

  • Reordering items by dragging them by enabling allowsReordering
  • Deleting items via backspace by enabling allowsDeleting
  • Quicklooking items via spacebar by providing items conforming to QuicklookPreviewable
  • Right click menu provider for selected items

It includes handlers for:

  • Prefetching items
  • Selecting items
  • Reordering items
  • Deleting items
  • Highlighting items
  • Displaying items
  • Hovering items with the mouse.
  • Drag and drop of files from and to the collection view
  • Pinching of the collection view

TableViewDiffableDataSource

Simliar to CollectionViewDiffableDataSource.

Quicklook for NSTableView & NSCollectionView

NSCollectionView/NSTableView isQuicklookPreviewable enables quicklook of selected items/cells via spacebar.

There are several ways to provide quicklook previews (see FZQuicklook for an extended documentation on how to provide them):

  • NSCollectionViewItems's & NSTableCellView's var quicklookPreview: QuicklookPreviewable?
collectionViewItem.quicklookPreview = URL(fileURLWithPath: "someFile.png")
  • NSCollectionView's datasource collectionView(_ collectionView: NSCollectionView, quicklookPreviewForItemAt indexPath: IndexPath) & NSTableView's datasource tableView(_ tableView: NSTableView, quicklookPreviewForRow row: Int)
func collectionView(_ collectionView: NSCollectionView, quicklookPreviewForItemAt indexPath: IndexPath) -> QuicklookPreviewable? {
    let galleryItem = galleryItems[indexPath.item]
    return galleryItem.fileURL
}
  • A NSCollectionViewDiffableDataSource & NSTableViewDiffableDataSource with an ItemIdentifierType conforming to QuicklookPreviewable
struct GalleryItem: QuicklookPreviewable {
    let title: String
    let imageURL: URL
    
    // The file url for quicklook preview.
    let previewItemURL: URL? {
    return imageURL
    }
    
    // The quicklook preview title displayed on the top of the Quicklook panel.
    let previewItemTitle: String? {
    return title
    }
}

let itemRegistration = NSCollectionView.ItemRegistration<NSCollectionViewItem, GalleryItem>() {
    collectionViewItem, indexPath, galleryItem in 
    // configurate collectionViewItem …
}
  
collectionView.dataSource = NSCollectionViewDiffableDataSource<Section, GalleryItem>(collectionView: collectionView, itemRegistration: ItemRegistration)

collectionView.quicklookSelectedItems()