如果您对一种面向协议的方法感兴趣,该方法允许简单地保存到多个具有不同名称的专辑,这些专辑在 Swift 4 中是最新的并避免使用单例,然后继续阅读。
此方法检查并获得用户授权,检查或创建相册,然后将图像保存到请求的相册中。如果在任何时候触发了错误,则会运行完成块并显示相应的错误。
这种方法的一个优点是,在创建实例后不会提示用户访问照片,而是在他们实际尝试保存图像时提示他们(如果需要授权)。
这个方法还可以让你定义一个非常简单的类来封装相册,符合PhotoAlbumHandler协议,从而免费获取所有相册交互逻辑,像这样:
class PhotoAlbum: PhotoAlbumHandler {
var albumName: String
init(named: String) {
albumName = named
}
}
然后,您还可以创建一个枚举来管理和封装您的所有相册。添加对另一个专辑的支持就像向枚举添加一个新案例并定义相应的专辑名称一样简单。
像这样:
public enum PhotoAlbums {
case landscapes
case portraits
var albumName: String {
switch self {
case .landscapes: return "Landscapes"
case .portraits: return "Portraits"
}
}
func album() -> PhotoAlbumHandler {
return PhotoAlbum.init(named: albumName)
}
}
使用这种方法可以轻松管理您的相册,在您的 viewModel(或视图控制器,如果您不使用视图模型)中,您可以像这样创建对相册的引用:
let landscapeAlbum = PhotoAlbums.landscapes.album()
let portraitAlbum = PhotoAlbums.portraits.album()
然后要将图像保存到其中一个相册,您可以执行以下操作:
let photo: UIImage = UIImage.init(named: "somePhotoName")
landscapeAlbum.save(photo) { (error) in
DispatchQueue.main.async {
if let error = error {
// show alert with error message or...???
self.label.text = error.message
return
}
self.label.text = "Saved image to album"
}
}
对于错误处理,我选择将任何可能的错误封装在错误枚举中:
public enum PhotoAlbumHandlerError {
case unauthorized
case authCancelled
case albumNotExists
case saveFailed
case unknown
var title: String {
return "Photo Save Error"
}
var message: String {
switch self {
case .unauthorized:
return "Not authorized to access photos. Enable photo access in the 'Settings' app to continue."
case .authCancelled:
return "The authorization process was cancelled. You will not be able to save to your photo albums without authorizing access."
case .albumNotExists:
return "Unable to create or find the specified album."
case .saveFailed:
return "Failed to save specified image."
case .unknown:
return "An unknown error occured."
}
}
}
定义接口的协议和处理与系统相册功能交互的协议扩展在这里:
import Photos
public protocol PhotoAlbumHandler: class {
var albumName: String { get set }
func save(_ photo: UIImage, completion: @escaping (PhotoAlbumHandlerError?) -> Void)
}
extension PhotoAlbumHandler {
func save(_ photo: UIImage, completion: @escaping (PhotoAlbumHandlerError?) -> Void) {
// Check for permission
guard PHPhotoLibrary.authorizationStatus() == .authorized else {
// not authorized, prompt for access
PHPhotoLibrary.requestAuthorization({ [weak self] status in
// not authorized, end with error
guard let strongself = self, status == .authorized else {
completion(.authCancelled)
return
}
// received authorization, try to save photo to album
strongself.save(photo, completion: completion)
})
return
}
// check for album, create if not exists
guard let album = fetchAlbum(named: albumName) else {
// album does not exist, create album now
createAlbum(named: albumName, completion: { [weak self] success, error in
// album not created, end with error
guard let strongself = self, success == true, error == nil else {
completion(.albumNotExists)
return
}
// album created, run through again
strongself.save(photo, completion: completion)
})
return
}
// save the photo now... we have permission and the desired album
insert(photo: photo, in: album, completion: { success, error in
guard success == true, error == nil else {
completion(.saveFailed)
return
}
// finish with no error
completion(nil)
})
}
internal func fetchAlbum(named: String) -> PHAssetCollection? {
let options = PHFetchOptions()
options.predicate = NSPredicate(format: "title = %@", named)
let collection = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .any, options: options)
guard let album = collection.firstObject else {
return nil
}
return album
}
internal func createAlbum(named: String, completion: @escaping (Bool, Error?) -> Void) {
PHPhotoLibrary.shared().performChanges({
PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: named)
}, completionHandler: completion)
}
internal func insert(photo: UIImage, in collection: PHAssetCollection, completion: @escaping (Bool, Error?) -> Void) {
PHPhotoLibrary.shared().performChanges({
let request = PHAssetChangeRequest.creationRequestForAsset(from: photo)
request.creationDate = NSDate.init() as Date
guard let assetPlaceHolder = request.placeholderForCreatedAsset,
let albumChangeRequest = PHAssetCollectionChangeRequest(for: collection) else {
return
}
let enumeration: NSArray = [assetPlaceHolder]
albumChangeRequest.addAssets(enumeration)
}, completionHandler: completion)
}
}
如果您想查看一个示例 Xcode 项目,可以在这里找到:https://github.com/appteur/ios_photo_album_sample