Swift 2.x 到 swift 3,XCode 抱怨:无法转换 PHFetchResult 类型的值

Swift 2.x to swift 3, XCode complaining : Cannot convert value of type PHFetchResult

这是我的代码:

    var galleryController: GalleryController!
    var albums: PHFetchResult = PHFetchResult()
    var cameraRoll: [PHFetchResult] = [PHFetchResult]()

    albums=getAlbums()
    cameraRoll=getcameraRoll()

func getAlbums()->PHFetchResult<AnyObject>
{
    let fetchOptions=PHFetchOptions()
    fetchOptions.predicate=NSPredicate(format:"estimatedAssetCount > 0")
    return PHAssetCollection.fetchAssetCollections(with: PHAssetCollectionType.album, subtype: PHAssetCollectionSubtype.albumSyncedAlbum, options: fetchOptions) as! PHFetchResult<AnyObject>
}    

func getcameraRoll()->[PHFetchResult<AnyObject>]
{
    self.photosFromCameraRoll=PHAsset.fetchAssets(with: PHAssetMediaType.image, options: nil)
    return [self.photosFromCameraRoll]
}

因为行 var albums: PHFetchResult = PHFetchResult() Xcode 符合这条消息:"Cannot convert value of type PHFetchResult<_> to specified PHFetchResult"

因为行 var cameraRoll: [PHFetchResult] = [PHFetchResult]() Xcode 也符合相同的信息:"Cannot convert value of type PHFetchResult<_> to specified PHFetchResult"

在Swift3中,PHFetchResult是泛型class。 (自去年 iOS 9 SDK 和 Swift 3 now imports ObjC generics 以来,它一直在 ObjC 中。)泛型类型必须始终是专用的。在 ObjC 中,泛型 class 可以使用或不使用类型参数(也就是说,您可以使用 PHFetchResult<SomeObjectType *> *PHFetchResult *,在后一种情况下,类型参数假定为 id).但是在 Swift 中你不能——你总是必须声明一个元素类型。

因此,如果您要声明一个变量来保存照片提取结果,则需要声明这些提取结果的预期对象类型。

  • 您的第一个看起来应该是专辑列表 — 可能会通过 fetchTopLevelUserCollections(with:) 获取?所以它的类型应该是 PHFetchResult<PHCollection>PHFetchResult<PHAssetCollection> 取决于你使用的是哪种获取方法以及你接下来打算用它做什么。

  • 第二个是fetch结果数组?考虑到变量名称 cameraRoll,仅此一项听起来很可疑。如果你只想获取相机胶卷中的所有资产,只需使用 fetchAssets(in:options:), passing the camera roll as the collection to fetch from. (You can get the camera roll with fetchAssetCollections(with:subtype:options:),使用 .smartAlbum 类型和 .smartAlbumUserLibrary 子类型。)

    无论是单个抓取结果还是抓取结果数组,如果您希望抓取包含资产,则类型(或数组的元素类型)应为 PHFetchResult<PHAsset>.


您的代码的第二个更小的问题是,您正在创建虚拟的、空的提取结果,只是为了它们(大概)被稍后的实际提取覆盖。 (PHFetchResult 是一个不可变的集合,因此它的初始化程序会创建一个空集合,您无法对其进行任何操作。)

你最好用可选类型声明这些变量(或者,如果你计划尽早初始化它们并期望之后的所有代码路径都在初始化之后出现,则隐式解包可选类型)。或者,如果它适合您的用例,让它们变得懒惰:

class PhotosAlbumsController: UITableViewController {

    var galleryController: GalleryController!

    lazy var albums = PHCollection.fetchTopLevelUserCollections(with: nil)
    // ^- implicitly typed PHFetchResult<PHCollection>

    lazy var cameraRoll = PHAssetCollection.fetchAssetCollections(with: .smartAlbum,
        subtype: .smartAlbumUserLibrary, options: nil).firstObject!
    // ^- implicitly typed PHAssetCollection
    // safe to force unwrap because there's always a Camera Roll / Saved Photos album

    // ...
}

这里延迟初始化(无论是使用 lazy 还是通过在其他时间直接获取)要注意的一件事是确保您首先拥有照片访问的用户授权,and/or 确保先注册为 PHPhotoLibrary 观察员。如果您在获得授权之前获取,您的获取将为空。如果您首先注册为观察者,您的提取仍然是空的,但一旦用户接受授权,您就会收到一个 photoLibraryDidChange 调用来填充它们。