Exhibition/ImageListController.swift
/* |
Copyright (C) 2016 Apple Inc. All Rights Reserved. |
See LICENSE.txt for this sample’s licensing information |
Abstract: |
Contains the `ImageListController` view controller subclass that displays the images in an `ImageCollection`. |
*/ |
import Cocoa |
/** |
`ImageListController` displays the `ImageFiles` in a given `ImageCollection` in |
an `NSCollectionView. It can be given a selection handler to report when an |
`ImageFile` is selected. |
*/ |
class ImageListController: NSViewController, NSCollectionViewDataSource, NSCollectionViewDelegate { |
// MARK: Properties |
override var nibName: String { |
return "ImageListController" |
} |
fileprivate static let imageCollectionViewItemIdentifier = "ImageItem" |
@IBOutlet var collectionView: NSCollectionView! |
// MARK: Image File / Collection Management |
var imageSelectionHandler: ((ImageFile?) -> Void)? |
var imageCollections = [ImageCollection]() { |
didSet { |
// Observe the new collections for changes, and unobserve the old collections. |
for collection in oldValue { |
NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: ImageCollection.imagesDidChangeNotification), object: collection) |
} |
for collection in imageCollections { |
NotificationCenter.default.addObserver(self, selector: #selector(ImageListController.imageCollectionDidChange(_:)), name: NSNotification.Name(rawValue: ImageCollection.imagesDidChangeNotification), object: collection) |
} |
guard isViewLoaded else { return } |
reloadCollectionViewAndSelectFirstItemIfNecessary() |
} |
} |
@objc func imageCollectionDidChange(_ notification: Notification) { |
reloadCollectionViewAndSelectFirstItemIfNecessary() |
} |
deinit { |
NotificationCenter.default.removeObserver(self) |
} |
// MARK: Life Cycle |
override func viewDidLoad() { |
// Tell the CollectionView to use the ImageCollectionViewItem nib for its items. |
let nib = NSNib(nibNamed: "ImageCollectionViewItem", bundle: nil) |
collectionView.register(nib, forItemWithIdentifier: ImageListController.imageCollectionViewItemIdentifier) |
collectionView.dataSource = self |
collectionView.delegate = self |
// Create a grid layout that is somewhat flexible for the various widths it might have. |
let gridLayout = NSCollectionViewGridLayout() |
gridLayout.minimumItemSize = NSSize(width: 100, height: 100) |
gridLayout.maximumItemSize = NSSize(width: 175, height: 175) |
gridLayout.minimumInteritemSpacing = 10 |
gridLayout.margins = EdgeInsets(top: 10, left: 10, bottom: 10, right: 10) |
collectionView.collectionViewLayout = gridLayout |
reloadCollectionViewAndSelectFirstItemIfNecessary() |
super.viewDidLoad() |
} |
fileprivate func reloadCollectionViewAndSelectFirstItemIfNecessary() { |
collectionView.reloadData() |
// If the `collectionView` has no selection, attempt to select the first available item. |
guard collectionView.selectionIndexPaths.isEmpty else { return } |
// Get the collections that contain images. |
let populatedCollections = imageCollections.filter { !$0.images.isEmpty } |
// Find the first `ImageCollection` that actually has images displayed. |
guard let firstPopulatedCollection = populatedCollections.first else { return } |
/* |
Get the index path to that collection's first image -- section is the |
index of the collection, item index is 0. |
*/ |
let firstPopulatedIndex = imageCollections.index(of: firstPopulatedCollection)! |
// Programmatically change the selection, and handle the changed selection. |
collectionView.selectionIndexPaths = [ |
IndexPath(item: 0, section: firstPopulatedIndex) |
] |
handleSelectionChanged() |
} |
// MARK: Collection View Data Source / Delegate |
func numberOfSections(in collectionView: NSCollectionView) -> Int { |
return imageCollections.count |
} |
func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int { |
return imageCollections[section].images.count |
} |
func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem { |
let item = collectionView.makeItem(withIdentifier: ImageListController.imageCollectionViewItemIdentifier, for: indexPath) |
if let imageItem = item as? ImageCollectionViewItem { |
// The section maps to the index of the `ImageCollection`. |
let imageCollection = imageCollections[(indexPath as NSIndexPath).section] |
imageItem.imageFile = imageCollection.images[(indexPath as NSIndexPath).item] |
} |
return item |
} |
func collectionView(_ collectionView: NSCollectionView, didSelectItemsAt indexPaths: Set<IndexPath>) { |
handleSelectionChanged() |
} |
func collectionView(_ collectionView: NSCollectionView, didDeselectItemsAt indexPaths: Set<IndexPath>) { |
handleSelectionChanged() |
} |
fileprivate func handleSelectionChanged() { |
guard let imageSelectionHandler = imageSelectionHandler else { return } |
/* |
The collection view does not support multiple selection, so just check |
the first index. |
*/ |
let selectedImage: ImageFile? |
if let selectedIndexPath = collectionView.selectionIndexPaths.first |
, (selectedIndexPath as NSIndexPath).section != -1 && (selectedIndexPath as NSIndexPath).item != -1 { |
// There is a selected index path, get the image at that path. |
selectedImage = imageCollections[(selectedIndexPath as NSIndexPath).section].images[(selectedIndexPath as NSIndexPath).item] |
} |
else { |
/* |
There is no selected index path -- the collection view supports |
empty selection, there is no selected image. |
*/ |
selectedImage = nil |
} |
imageSelectionHandler(selectedImage) |
} |
} |
Copyright © 2016 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2016-09-28