这是一个易于使用的即插即用框架,提供用于拍照和从相册选择资源用户界面的用户界面。用户界面设计用于支持类似“键盘”的 inputView
展示,用于聊天用户界面。项目使用 Swift4 编写。
功能
- 为聊天应用以及常规视图控制器设计的展示
- 纵向和横向模式(支持 iPhone X)
- 捕获资源(照片、实况照片、视频)
- 将捕获的资源保存到相册
- 切换前后摄像头
- 打开/关闭实况照片
- 高度可定制的布局和 UI
要求
- iOS 10.1+
- Xcode 9+
- Swift 4
安装
- Carthage
- 手动
概述
A central object ImagePickerController
manages user interactions and delivers the results of those interactions to a delegate object. The role and appearance of an image picker controller depend on the configuration you set up before presenting it.
Image Picker consists of 3 main functional parts
- section of action items - supports up to 2 action buttons, this section is optional and by default contains action item for camera and photos.
- section of camera item - shows camera's video output and provides UI for user to take photos, videos, etc. This section is optinal and by default it's turned on.
- section of asset items - shows thumbnails of assets found in Photo Library allowing user to select them. Section is mandatory and and can not be turned off.
To use an image picker controller, you must provide a delegate that conforms to ImagePickerControllerDelegate
protocol. Use delegate to get informed when user takes a picture or selects an asset from library and configure custom action and asset collection view cells.
To use an image picker controller, perform these steps
- register permissions in your
info.plist
file (see Permissions section for more info.) - create new instance of ImagePickerController
- optionally configure appearance, layout, custom cells and capture mode
- present image picker's view controller
权限
iOS 需要你在 info.plist 中注册以下权限键
NSPhotoLibraryUsageDescription
- 用于从设备的照片库中显示照片和视频NSCameraUsageDescription
- 用于拍照和录制视频NSMicrophoneUsageDescription
- 用于录制带音频的短视频或Live照片
应用仅在需要时自动请求权限。
配置
支持各种类型的配置。所有配置应该在首次访问视图控制器的视图之前完成。
- 要配置要捕获的媒体类型,请使用
CaptureSettings
- 要配置通用视觉外观,请使用
Appearance
类 - 要配置动作、相机和资产项的布局,请使用
LayoutConfiguration
类。 - 要为动作、相机和资产项使用自定义视图,请使用
CellRegistrator
类 - 如果需要,不要忘记设置您的
delegate
和dataSource
- 要定义应可用于选择的照片源,请使用视图控制器的
assetsFetchResultBlock
块
捕捉设置
当前图像选择器支持捕捉 照片、Live照片和视频。
要配置图像选择器以支持所需的媒体类型,请使用 CaptureSettings
结构体。使用属性 cameraMode
指定您感兴趣的类型。如果您根本不支持Live照片,请使用值 photo
,否则使用 photoAndLivePhoto
。如果您想捕获照片和视频,请使用 photoAndVideo
。同时捕捉视频和Live照片不支持,并且配置后无法在预设之间切换。
默认情况下,所有捕获的照片都不会保存在照片库中,而是直接通过代理提供给您。但是,如果您希望将照片保存在照片库中,请将 savesCapturedPhotosToPhotoLibrary
设置为 true。Live照片和视频会自动保存在照片库中。
同时拍摄照片和Live照片并将其保存在照片库中的配置示例
let imagePicker = ImagePickerController()
imagePicker.captureSettings.cameraMode = .photoAndLivePhoto
imagePicker.captureSettings.savesCapturedPhotosToPhotoLibrary = true
请参阅 CaptureSettings
公共头文件以获取更多信息。
提供自己的照片获取结果
默认情况下,Image Picker 会从智能相册 smartAlbumUserLibrary
获取 1000 张照片和视频,该相册应代表 相机胶卷 专辑。如果您要提供自己的获取结果,请实现图像选择控制器中的 assetsFetchResultBlock
块。
例如,要获取仅动态照片,您可以使用以下代码片段:
let imagePicker = ImagePickerController()
imagePicker.assetsFetchResultBlock = {
guard let livePhotosCollection = PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype: .smartAlbumLivePhotos, options: nil).firstObject else {
return nil //you can return nil if you did not find desired fetch result, default fetch result will be used.
}
return PHAsset.fetchAssets(in: livePhotosCollection, options: nil)
}
有关配置获取结果的更多信息,请参阅 Photos 框架文档。
使用外观进行样式设计
Image picker 视图层包含用于显示操作、相机和资产项的 UICollectionView
,以及用于显示权限状态的覆盖视图。当通过 CellRegistrator
提供自定义单元格时,您还需要负责进行样式设计以及为权限状态自定义覆盖视图。然而,仅支持少数样式属性,例如背景颜色。请使用自定义外观机制来实现所需的样式。
- 要全局样式化所有图像选择器,请使用全局外观代理对象
ImagePickerController.appearance().backgroundColor = UIColor.black
- 要样式化图像选择器的特定实例,请使用实例外观代理对象
let vc = ImagePickerController()
vc.appearance().backgroundColor = UIColor.black
有关默认样式属性和更多信息,请参阅 Appearance
类的公共接口。
请注意,当前的 UIKit 外观代理不支持。
使用 LayoutConfiguration 定义布局
Image picker 支持各种类型的布局,以及垂直和水平滚动方向。使用 LayoutConfiguration
,您可以为您的应用设置特定的布局。
- 操作项 总是显示为第一个部分,可以包含最多 2 个按钮。默认情况下,此部分显示 2 项。以下示例将展示如何关闭第二个操作项
let imagePicker = ImagePickerController()
imagePicker.layoutConfiguration.showsSecondActionItem = false
- 相机项 总是显示在操作项部分之后的一节。因此,如果操作项关闭,则此部分会显示为第一部分。相机项部分默认开启,因此如果您想关闭它,请使用以下代码
let imagePicker = ImagePickerController()
imagePicker.layoutConfiguration.showsCameraItem = false
请注意,如果您关闭相机部分,Image Picker 不会请求用户的相机权限。
- 资产项 总是显示,无论应用程序中是否有任何照片。您可以控制每一列或每一行(基于滚动方向)中的资产项的数量。默认情况下,每列或每行有 2 个资产。要将其更改为 1 个,请看以下片段
let imagePicker = ImagePickerController()
imagePicker.layoutConfiguration.numberOfAssetItemsInRow = 1
请注意,提供的值必须大于 0,否则将抛出异常。
- 其他布局属性
- interitemSpacing - 网格布局时项目之间的间距
- actionSectionSpacing - 动作项目部分与相机项目部分之间的间距
- cameraSectionSpacing - 相机部分与资产项目部分之间的间距
提供自定义视图
Image Picker 中使用的所有视图都可以由您提供,以实现高度可定制的 UI,使其最符合您的应用。如前所述,整个 UI 由一个集合视图和一个覆盖视图组成。
- 集合视图 使用单元格来显示动作、相机和资产项。默认情况下,Image Picker 为您提供了具有标准功能和 UI 的单元格。但是,如果您希望使用自己的包含您自己的 UI 和功能的单元格,请使用
CellRegistrator
。它包含用于注册每个部分类型的 nib 和类的 API。例如,要注册动作项部分的自定义单元格,请使用以下代码
let imagePicker = ImagePickerController()
imagePicker.cellRegistrator.registerNibForActionItems(UINib(nibName: "IconWithTextCell", bundle: nil))
同样的原则也适用于注册自定义相机和资产项。您还可以为每种资产媒体类型(例如图片和视频)设置特定的单元格。例如,要为视频资产使用特定的单元格,请使用
let imagePicker = ImagePickerController()
imagePicker.cellRegistrator.register(cellClass: CustomVideoCell.self, forAssetItemOf: .video)
imagePicker.cellRegistrator.register(cellClass: CustomImageCell.self, forAssetItemOf: .image)
注意: 如果您使用自定义单元格,请确保为所有媒体类型(音频、视频)注册单元格,否则 Image Picker 将抛出异常。请记住,相机项单元格 必须 继承自
CameraCollectionViewCell
,资产项单元格 必须 遵守ImagePickerAssetCell
协议。您还可以使用代理精细调整您的资产单元格以特定资产类型,例如实时照片、全景照片等。请参阅我们的 ExampleApp 以获取实现细节。
- 覆盖视图 在应用无法访问 照片库 的权限的情况下显示在集合视图上。要支持覆盖视图,请实现符合
ImagePickerControllerDatasource
协议的数据源。可能的实现如下
extension ViewController: ImagePickerControllerDataSource {
func imagePicker(controller: ImagePickerController, viewForAuthorizationStatus status: PHAuthorizationStatus) -> UIView {
let statusView = CustomPermissionStatusView(frame: .zero)
//configure and return view based on authorization status
return statusView
}
}
实现自定义动作单元格
如果您希望使用自己的动作项单元格,请向 CellRegistrator
注册您的单元格类或 nib。然后实现相应的 ImagePickerControllerDelegate
方法来配置单元格在显示之前。
- 使用布局配置来设置您想要的动作项数量
let imagePicker = ImagePickerController()
imagePicker.layoutConfiguration.showsFirstActionItem = true
imagePicker.layoutConfiguration.showsSecondActionItem = true
- 在单元格注册器上注册您的动作单元格,例如
imagePicker.registerCellClassForActionItems(IconWithTextCell.self)
- 通过实现代理方法配置单元格,例如
func imagePicker(controller: ImagePickerController, willDisplayActionItem cell: UICollectionViewCell, at index: Int) {
switch cell {
case let iconWithTextCell as IconWithTextCell:
switch index {
case 0:
iconWithTextCell.titleLabel.text = "Camera"
iconWithTextCell.imageView.image = #imageLiteral(resourceName: "ic-camera")
case 1:
iconWithTextCell.titleLabel.text = "Photo Library"
iconWithTextCell.imageView.image = #imageLiteral(resourceName: "ic-photo")
default: break
}
default:
break
}
}
- 通过实现代理方法处理动作
func imagePicker(controller: ImagePickerController, didSelectActionItemAt index: Int) {
print("did select action \(index)")
}
实现自定义相机单元格
Image picker 提供了一个默认的相机单元格,它根据 captureSettings
来适应拍照、实时照片或视频。
如果您想实现更复杂的特性,您必须提供自己的子类或 nib 文件,该文件包含从 CameraCollectionViewCell
继承的定制单元类并实现专用方法。
注意:请注意,定制的 nib 单元类必须从
CameraCollectionViewCell
继承,且不能指定任何重用标识符。图像选择器内部处理重用标识符。
可以完全自定义 UI 的支持特性
- 拍摄照片、实时照片、录制视频、翻转镜头
- 提供自定义按钮(相机翻转、拍摄照片、录制视频)
- 更新实时照片状态
- 更新录制状态
- 显示当前相机访问权限
要查看支持所有所述特性的自定义实现示例,请参阅 Image Picker 源代码中的类 LivePhotoCameraCell
和 VideoCameraCell
。
实现自定义资源单元
图像选择器提供了一个默认的资源单元,显示图片缩略图、选中状态。如果是视频,它将显示图标和持续时间;如果是实时照片,它将显示图标。如果您想提供自定义资源单元,例如显示资产的媒体子类型(实时照片、全景、HDR、屏幕截图、流视频等),请简单地在 CellRegistrator
上注册自己的资源单元,它符合 ImagePickerAssetCell
并在实现图像选择器代理的 func imagePicker(controller: ImagePickerController, willDisplayAssetItem cell: ImagePickerAssetCell, asset: PHAsset)
方法。可能的实现示例可以是
- 为每种资产媒体类型注册单元格类,例如
let imagePicker = ImagePickerController()
imagePicker.register(cellClass: CustomImageCell.self, forAssetItemOf: .image)
imagePicker.register(cellClass: CustomVideoCell.self, forAssetItemOf: .video)
请注意,
CellRegistrator
提供了一个方法来为任何资产媒体类型注册 1 个单元格或 nib。
- 实现代理方法以配置您的资产单元格,例如
func imagePicker(controller: ImagePickerController, willDisplayAssetItem cell: ImagePickerAssetCell, asset: PHAsset) {
switch cell {
case let videoCell as CustomVideoCell:
videoCell.label.text = ViewController.durationFormatter.string(from: asset.duration)
case let imageCell as CustomImageCell:
if asset.mediaSubtypes.contains(.photoLive) {
imageCell.subtypeImageView.image = #imageLiteral(resourceName: "icon-live")
}
else if asset.mediaSubtypes.contains(.photoPanorama) {
imageCell.subtypeImageView.image = #imageLiteral(resourceName: "icon-pano")
}
else if #available(iOS 10.2, *), asset.mediaSubtypes.contains(.photoDepthEffect) {
imageCell.subtypeImageView.image = #imageLiteral(resourceName: "icon-depth")
}
default:
break
}
}
要查看支持所有所述特性的自定义实现示例,请参阅 Image Picker 源代码中的类 VideoAssetCell
和 AssetCell
。
展示
如果您想以默认设置展示图像选择器,您不需要进行任何特殊配置,只需创建一个新的实例并向视图控制器展示。
let imagePicker = ImagePickerController()
navigationController.present(imagePicker, animated: true, completion: nil)
但是,大多数情况下,您将想要进行自定义配置,因此请在视图控制器的视图加载之前(在调用 viewDidLoad()
方法之前)进行所有配置。
let imagePicker = ImagePickerController()
imagePicker.cellRegistrator ...
imagePicker.layoutConfiguration ...
imagePicker.captureSettings ...
imagePicker.appearance...
imagePicker.dataSource = ...
imagePicker.delegate = ...
navigationController.present(imagePicker, animated: true, completion: nil)
如果您想在聊天应用中将图像选择器展示为“键盘”,您必须设置视图控制器的视图为您第一个响应者的 inputView,并且
- 如果视图的高度应该是默认键盘高度,请将视图的自动缩放掩码设置为
.flexibleHeight
- 将视图的
frame.size.height
设置为固定高度。要了解如何将图像选择器设置为视图控制器的输入视图的示例,请参阅我们的示例应用。
可选地,在展示图像选择器之前,您可以使用 PHPhotoLibrary
API 检查用户是否已授予对照片库的访问权限,并请求权限。如果您不这样做,图像选择器会在展示后为您自动处理此操作。
访问、选择和取消选择资产项
所有用户操作,例如选择/取消选择资产、拍摄新照片或Live照片或录制视频,都使用ImagePickerControllerDelegate
委托方法进行公告。有关列表和更详细的说明,请参阅公共头文件。
有时您可能需要以编程方式管理已选择资产。图片选择器提供了几个方便的方法来处理资产项。
selectedAssets
属性返回一个当前已选择PHAsset
项的数组- 要访问某个索引处的资产项,请使用
assets(at:)
和asset(at:)
- 要编程选择资产项,请使用
selectAsset(at:animated:scrollPosition:)
- 要编程取消选择资产项,请使用
deselectAsset(at:animated:)
- 要编程取消选择所有已选择项,请使用
deselectAllAssets(_:)