尝试显示照片库中的照片。下面的 Apple 示例代码无法正常工作。它不会等待通知,而是尝试启动在“部分中的项目数”处崩溃的集合视图,因为没有要显示的照片数据。
感谢您 Swift 大师的任何帮助!
/*
Copyright (C) 2016 Apple Inc. All Rights Reserved.
See LICENSE.txt for this sample’s licensing information
Abstract:
The `ViewController` class is a subclass to NSViewController responsible for managing the app's content.
*/
import Cocoa
import MediaLibrary
class IconViewBox : NSBox {
override func hitTest(_ aPoint: NSPoint) -> NSView? {
// Don't allow any mouse clicks for subviews in this NSBox.
return nil
}
}
// MARK: -
class ViewController: NSViewController, NSCollectionViewDelegate {
// MARK: - Types
// Keys describing the dictionary for each photo loaded.
private struct ItemKeys {
static let imageKey = "icon"
static let nameKey = "name"
}
// MLMediaLibrary property values for KVO.
private struct MLMediaLibraryPropertyKeys {
static let mediaSourcesKey = "mediaSources"
static let rootMediaGroupKey = "rootMediaGroup"
static let mediaObjectsKey = "mediaObjects"
static let contentTypeKey = "contentType"
}
// MARK: - Properties
/**
The KVO contexts for `MLMediaLibrary`.
This provides a stable address to use as the `context` parameter for KVO observation methods.
*/
private var mediaSourcesContext = 1
private var rootMediaGroupContext = 2
private var mediaObjectsContext = 3
private var photoSize = CGSize(width: 168, height: 145)
// Contains an array of dictionaries describing each photo (refer to ItemKeys for key/values).
@IBOutlet weak var arrayController: NSArrayController!
@IBOutlet weak var collectionView: NSCollectionView!
@IBOutlet private weak var noPhotosLabel: NSTextField!
@IBOutlet private weak var activityIndicator: NSProgressIndicator!
// MLMediaLibrary instances for loading the photos.
private var mediaLibrary: MLMediaLibrary!
private var mediaSource: MLMediaSource!
private var rootMediaGroup: MLMediaGroup!
// MARK: - View Controller Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
print ("VDL1...................")
// Start progress indicator in case fetching the photos from the photo library takes time.
self.activityIndicator.isHidden = false
self.activityIndicator.startAnimation(self)
self.collectionView.minItemSize = self.photoSize
self.collectionView.maxItemSize = self.photoSize
self.arrayController.setSelectionIndex(-1) // No selection to start out with.
// Setup the media library to load only photos, don't include other source types.
let options: [String : AnyObject] =
[MLMediaLoadSourceTypesKey: MLMediaSourceType.image.rawValue as AnyObject,
MLMediaLoadIncludeSourcesKey: [MLMediaSourcePhotosIdentifier, MLMediaSourceiPhotoIdentifier] as AnyObject]
// Create our media library instance to get our photo.
mediaLibrary = MLMediaLibrary(options: options)
// We want to be called when media sources come in that's available (via observeValueForKeyPath).
self.mediaLibrary.addObserver(self,
forKeyPath: MLMediaLibraryPropertyKeys.mediaSourcesKey,
options: NSKeyValueObservingOptions.new,
context: &mediaSourcesContext)
if (self.mediaLibrary.mediaSources != nil) {
print ("VDL2...................")
} // Reference returns nil but starts the asynchronous loading.
}
deinit {
// Make sure to remove us as an observer before "mediaLibrary" is released.
self.mediaLibrary.removeObserver(self, forKeyPath: MLMediaLibraryPropertyKeys.mediaSourcesKey, context:&mediaSourcesContext)
}
// MARK: - NSCollectionViewDataSource
func numberOfSectionsInCollectionView(_ collectionView: NSCollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
let photos = self.arrayController.arrangedObjects as! NSArray
return photos.count
}
func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAtIndexPath indexPath: IndexPath) -> NSCollectionViewItem {
let item = collectionView.makeItem(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "IconItem"), for:indexPath)
let photos = self.arrayController.arrangedObjects as! NSArray
let iconInfo = photos[(indexPath as NSIndexPath).item]
item.representedObject = iconInfo
return item
}
// MARK: - NSCollectionViewDelegate
func collectionView(_ collectionView: NSCollectionView, didSelectItemsAt indexPaths: Set<IndexPath>) {
if let itemIndexPath = indexPaths.first {
let photos = self.arrayController.arrangedObjects as! NSArray
let itemDict = photos[((itemIndexPath as NSIndexPath).item)] as! NSDictionary
if let itemTitle = itemDict[ItemKeys.nameKey] as? String {
if (itemTitle.characters.count > 0) {
print("selected photo: '\(itemTitle)'")
}
else {
print("selected photo: <no title>")
}
}
}
}
// MARK: - Utilities
/// Helps to make sure the media object is the photo format we want.
private func isValidImage(_ mediaObject: MLMediaObject) -> Bool {
var isValidImage = false
let attrs = mediaObject.attributes
let contentTypeStr = attrs[MLMediaLibraryPropertyKeys.contentTypeKey] as! String
// We only want photos, not movies or older PICT formats (PICT image files are not supported in a sandboxed environment).
if ((contentTypeStr != kUTTypePICT as String) && (contentTypeStr != kUTTypeQuickTimeMovie as String))
{
isValidImage = true
}
return isValidImage
}
/// Obtains the title of the MLMediaObject (either the meta name or the last component of the URL).
func imageTitle(_ fromMediaObject: MLMediaObject) -> String {
guard let title = fromMediaObject.attributes["name"] else {
return fromMediaObject.url!.lastPathComponent
}
return title as! String
}
// MARK: - Photo Loading
/// Observer for all key paths returned from the MLMediaLibrary.
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
print ("Observe1...................")
if (keyPath == MLMediaLibraryPropertyKeys.mediaSourcesKey && context == &mediaSourcesContext && object! is MLMediaLibrary) {
// The media sources have loaded, we can access the its root media.
if let mediaSource = self.mediaLibrary.mediaSources?[MLMediaSourcePhotosIdentifier] {
self.mediaSource = mediaSource
}
else if let mediaSource = self.mediaLibrary.mediaSources?[MLMediaSourceiPhotoIdentifier] {
self.mediaSource = mediaSource
}
else {
print ("Observe2...................")
// Can't find any media sources.
self.noPhotosLabel.isHidden = false
// Stop progress indicator.
self.activityIndicator.isHidden = true
self.activityIndicator.stopAnimation(self)
return // No photos found.
}
// Media Library is loaded now, we can access mediaSource for photos
self.mediaSource.addObserver(self,
forKeyPath: MLMediaLibraryPropertyKeys.rootMediaGroupKey,
options: NSKeyValueObservingOptions.new,
context: &rootMediaGroupContext)
// Obtain the media grouping (reference returns nil but starts asynchronous loading).
if (self.mediaSource.rootMediaGroup != nil) {}
}
else if (keyPath == MLMediaLibraryPropertyKeys.rootMediaGroupKey && context == &rootMediaGroupContext && object! is MLMediaSource) {
print ("Observe3...................")
// The root media group is loaded, we can access the media objects.
// Done observing for media groups.
self.mediaSource.removeObserver(self, forKeyPath: MLMediaLibraryPropertyKeys.rootMediaGroupKey, context:&rootMediaGroupContext)
self.rootMediaGroup = self.mediaSource.rootMediaGroup
self.rootMediaGroup.addObserver(self,
forKeyPath: MLMediaLibraryPropertyKeys.mediaObjectsKey,
options: NSKeyValueObservingOptions.new,
context: &mediaObjectsContext)
// Obtain the all the photos, (reference returns nil but starts asynchronous loading).
if (self.rootMediaGroup.mediaObjects != nil) {}
}
else if (keyPath == MLMediaLibraryPropertyKeys.mediaObjectsKey && context == &mediaObjectsContext && object! is MLMediaGroup) {
print ("Observe4...................")
// The media objects are loaded, we can now finally access each photo.
// Done observing for media objects that group.
self.rootMediaGroup.removeObserver(self, forKeyPath: MLMediaLibraryPropertyKeys.mediaObjectsKey, context:&mediaObjectsContext)
// Stop progress indicator since we know if we have photos (or not).
self.activityIndicator.isHidden = true
self.activityIndicator.stopAnimation(self)
let mediaObjects = self.rootMediaGroup.mediaObjects
if (mediaObjects != nil && mediaObjects!.count > 0) {
print ("Observe5...................")
// Add photos to the array, to be used in our NSCollectionView.
for mediaObject in mediaObjects! {
if (self.isValidImage(mediaObject)) { // Make sure the media object is a photo.
let title = self.imageTitle(mediaObject)
if let image = NSImage.init(contentsOf: mediaObject.thumbnailURL!) {
let iconItem : Dictionary = [ItemKeys.imageKey: image, ItemKeys.nameKey: title] as [String : Any]
self.arrayController.addObject(iconItem)
}
}
}
self.collectionView.reloadData()
}
else {
// No photos available.
self.noPhotosLabel.isHidden = false
}
self.rootMediaGroup = nil // We are done with this.
}
else {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
}
}
}