TZImagePickerController与Swift项目集成指南:从配置到高级功能全解析
你是否还在为iOS项目中的图片选择功能开发而烦恼?既要支持多选、原图、视频,又要实现预览和裁剪,原生UIImagePickerController根本满足不了需求?本文将带你一步到位解决这些问题,通过详细的代码示例和配置说明,让你在Swift项目中轻松集成TZImagePickerController这个强大的图片选择框架。读完本文,你将掌握从基础配置、权限处理到高级定制的全流程,彻底解决图片选择功能开发的痛点。
一、框架简介与核心优势
TZImagePickerController是一个功能强大的图片选择框架(Image Picker Controller),作为原生UIImagePickerController的替代品,它提供了更丰富的功能和更灵活的定制选项。该框架支持多选图片、选择原图、视频选择,同时包含预览、裁剪等功能,最低支持iOS 10+系统。
1.1 核心功能对比
| 功能 | 原生UIImagePickerController | TZImagePickerController |
|---|---|---|
| 多选图片 | ❌ 不支持 | ✅ 支持,可自定义最大选择数量 |
| 原图选择 | ❌ 不支持 | ✅ 支持,可控制是否显示原图选项 |
| 视频选择 | ⚠️ 有限支持 | ✅ 完整支持,可选择多个视频 |
| 预览功能 | ⚠️ 基础支持 | ✅ 高级预览,支持缩放、滑动切换 |
| 图片裁剪 | ⚠️ 基础支持 | ✅ 支持矩形和圆形裁剪,自定义尺寸 |
| GIF支持 | ❌ 不支持 | ✅ 完整支持GIF预览和选择 |
| 自定义UI | ⚠️ 有限支持 | ✅ 全面支持,可自定义几乎所有UI元素 |
1.2 框架结构概览
TZImagePickerController的核心结构包含以下几个主要部分:
二、环境配置与集成步骤
2.1 系统要求与依赖
- 最低系统版本:iOS 10.0+
- 开发环境:Xcode 10.0+
- 依赖框架:Photos.framework、PhotosUI.framework
2.2 集成方式选择
TZImagePickerController提供了多种集成方式,可根据项目需求选择最合适的方式:
2.2.1 CocoaPods集成(推荐)
在Podfile中添加以下代码:
# 完整版本,包含所有功能
pod 'TZImagePickerController'
# 基础版本,不包含定位相关代码
# pod 'TZImagePickerController/Basic'
执行以下命令安装:
pod install
2.2.2 手动集成
- 从Git仓库克隆或下载源代码:
git clone https://gitcode.com/gh_mirrors/tz/TZImagePickerController.git
-
将TZImagePickerController文件夹拖拽到Xcode项目中,确保勾选"Copy items if needed"和对应的target。
-
在项目设置中,确保以下框架已添加到"Link Binary With Libraries":
- Photos.framework
- PhotosUI.framework
2.3 Info.plist配置
TZImagePickerController需要访问相册、相机、麦克风和位置等权限,需在Info.plist中添加以下配置:
<key>NSCameraUsageDescription</key>
<string>需要访问相机以拍摄照片和视频</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>需要访问相册以选择照片和视频</string>
<key>NSMicrophoneUsageDescription</key>
<string>需要访问麦克风以录制视频</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>需要访问位置以记录照片位置信息</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>需要访问相册以保存照片和视频</string>
<key>Prevent limited photos access alert</key>
<true/>
三、Swift项目集成实战
3.1 桥接文件配置
由于TZImagePickerController是用Objective-C编写的,在Swift项目中使用需要创建桥接文件(Bridging Header):
- 创建一个新的头文件,命名为
YourProjectName-Bridging-Header.h - 在项目Build Settings中,找到"Objective-C Bridging Header"设置,填入桥接文件路径
- 在桥接文件中添加以下导入:
#import "TZImagePickerController.h"
#import "TZImageManager.h"
#import "TZAssetModel.h"
3.2 基础使用示例
以下是一个基本的Swift集成示例,展示如何在Swift项目中调用TZImagePickerController:
import UIKit
import Photos
class ViewController: UIViewController, TZImagePickerControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
}
private func setupUI() {
view.backgroundColor = .white
let pickButton = UIButton(type: .system)
pickButton.setTitle("选择图片", for: .normal)
pickButton.addTarget(self, action: #selector(pickImageTapped), for: .touchUpInside)
pickButton.frame = CGRect(x: 100, y: 200, width: 200, height: 50)
view.addSubview(pickButton)
}
@objc private func pickImageTapped() {
// 创建图片选择器实例
let imagePicker = TZImagePickerController(maxImagesCount: 9, delegate: self)
// 设置选择器属性
imagePicker?.allowPickingOriginalPhoto = true
imagePicker?.allowPickingVideo = true
imagePicker?.allowPickingGif = true
// 设置完成回调
imagePicker?.setDidFinishPickingPhotosHandle({ (photos, assets, isSelectOriginal) in
// 处理选择的图片
print("选中了\(photos?.count ?? 0)张图片")
print("是否选择原图: \(isSelectOriginal)")
// 显示选中的图片
self.showSelectedImages(photos: photos)
})
// present选择器
if let picker = imagePicker {
present(picker, animated: true, completion: nil)
}
}
private func showSelectedImages(photos: [UIImage]?) {
// 实现显示选中图片的逻辑
guard let photos = photos else { return }
for (index, photo) in photos.enumerated() {
let imageView = UIImageView(image: photo)
imageView.frame = CGRect(x: 20 + (index % 3) * 100,
y: 300 + (index / 3) * 100,
width: 90, height: 90)
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
view.addSubview(imageView)
}
}
}
3.3 代理方法实现
除了使用block回调,还可以通过实现代理方法来处理选择结果:
// MARK: - TZImagePickerControllerDelegate
extension ViewController {
func imagePickerController(_ picker: TZImagePickerController!, didFinishPickingPhotos photos: [UIImage]!, sourceAssets assets: [Any]!, isSelectOriginalPhoto: Bool) {
print("代理方法:选中了\(photos.count)张图片")
showSelectedImages(photos: photos)
}
func imagePickerControllerDidCancel(_ picker: TZImagePickerController!) {
print("用户取消了选择")
}
func imagePickerController(_ picker: TZImagePickerController!, didFinishPickingVideo coverImage: UIImage!, sourceAssets asset: PHAsset!) {
print("选中了视频,时长:\(asset.duration)秒")
let imageView = UIImageView(image: coverImage)
imageView.frame = CGRect(x: 20, y: 300, width: 150, height: 150)
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
view.addSubview(imageView)
}
}
四、高级功能与定制
4.1 自定义选择器属性
TZImagePickerController提供了丰富的属性来自定义其行为:
// 创建选择器实例
let imagePicker = TZImagePickerController(maxImagesCount: 5, delegate: self)
// 基本设置
imagePicker?.columnNumber = 4 // 每行显示4列
imagePicker?.allowTakePicture = true // 允许拍照
imagePicker?.allowTakeVideo = true // 允许拍摄视频
imagePicker?.videoMaximumDuration = 15 // 视频最大拍摄时长15秒
imagePicker?.sortAscendingByModificationDate = false // 按修改时间降序排列
// 选择限制
imagePicker?.minPhotoWidthSelectable = 800 // 最小可选图片宽度
imagePicker?.minPhotoHeightSelectable = 600 // 最小可选图片高度
// 外观设置
imagePicker?.naviBgColor = .darkGray // 导航栏背景色
imagePicker?.naviTitleColor = .white // 导航栏标题颜色
imagePicker?.oKButtonTitleColorNormal = .systemBlue // 确认按钮颜色
imagePicker?.iconThemeColor = .systemGreen // 选中图标主题色
// 预览设置
imagePicker?.allowPreview = true // 允许预览
imagePicker?.photoPreviewMaxWidth = 1000 // 预览图片最大宽度
// 裁剪设置(单选模式下)
imagePicker?.allowCrop = true // 允许裁剪
imagePicker?.needCircleCrop = false // 不需要圆形裁剪
imagePicker?.cropRect = CGRect(x: 50, y: 200, width: 300, height: 300) // 裁剪框大小
4.2 单选与裁剪功能
设置单选模式并启用裁剪功能:
func showSingleImagePickerWithCrop() {
// 创建单选模式的图片选择器
let imagePicker = TZImagePickerController(maxImagesCount: 1, delegate: self)
// 启用裁剪功能
imagePicker?.allowCrop = true
imagePicker?.showSelectBtn = false // 隐藏选择按钮
// 设置裁剪参数
imagePicker?.needCircleCrop = false // 矩形裁剪
// 如果需要圆形裁剪,设置为true并指定半径
// imagePicker?.needCircleCrop = true
// imagePicker?.circleCropRadius = 100
// 设置裁剪框大小(竖屏)
let screenWidth = UIScreen.main.bounds.width
let cropWidth: CGFloat = screenWidth - 100
let cropRect = CGRect(x: 50,
y: (UIScreen.main.bounds.height - cropWidth) / 2,
width: cropWidth,
height: cropWidth)
imagePicker?.cropRect = cropRect
// 设置完成回调
imagePicker?.setDidFinishPickingPhotosHandle({ (photos, assets, isSelectOriginal) in
if let croppedImage = photos?.first {
// 显示裁剪后的图片
let imageView = UIImageView(image: croppedImage)
imageView.frame = CGRect(x: 50, y: 200, width: 200, height: 200)
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
self.view.addSubview(imageView)
}
})
if let picker = imagePicker {
present(picker, animated: true, completion: nil)
}
}
4.3 视频选择与编辑
配置视频选择和编辑功能:
func showVideoPicker() {
let imagePicker = TZImagePickerController(maxImagesCount: 1, delegate: self)
// 配置视频选择
imagePicker?.allowPickingVideo = true // 允许选择视频
imagePicker?.allowPickingImage = false // 不允许选择图片
imagePicker?.allowEditVideo = true // 允许编辑视频
imagePicker?.videoMaximumDuration = 30 // 最大视频时长30秒
// 设置视频编辑参数
imagePicker?.maxCropVideoDuration = 15 // 裁剪视频最大时长15秒
imagePicker?.presetName = AVAssetExportPresetMediumQuality // 导出质量
// 设置视频选择回调
imagePicker?.setDidFinishPickingPhotosHandle({ (photos, assets, isSelectOriginal) in
// 视频封面图在photos数组中
if let coverImage = photos?.first {
print("视频封面图获取成功")
// 显示封面图
let imageView = UIImageView(image: coverImage)
imageView.frame = CGRect(x: 50, y: 200, width: 200, height: 150)
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
self.view.addSubview(imageView)
}
// 视频资源在assets数组中
if let asset = assets?.first as? PHAsset {
print("选中视频时长: \(asset.duration)秒")
// 获取视频URL
TZImageManager.manager().getVideoWithAsset(asset, completion: { (videoUrl, error) in
if let url = videoUrl {
print("视频URL: \(url)")
// 在这里可以播放视频或上传
}
})
}
})
if let picker = imagePicker {
present(picker, animated: true, completion: nil)
}
}
4.4 UI定制
自定义导航栏和底部工具栏样式:
func customizeImagePickerUI() {
let imagePicker = TZImagePickerController(maxImagesCount: 9, delegate: self)
// 自定义导航栏
imagePicker?.naviBgColor = .white
imagePicker?.naviTitleColor = .black
imagePicker?.naviTitleFont = UIFont.boldSystemFont(ofSize: 18)
// 自定义按钮文字
imagePicker?.doneBtnTitleStr = "完成"
imagePicker?.cancelBtnTitleStr = "取消"
imagePicker?.previewBtnTitleStr = "预览"
// 自定义按钮颜色
imagePicker?.oKButtonTitleColorNormal = .systemBlue
imagePicker?.oKButtonTitleColorDisabled = .lightGray
imagePicker?.barItemTextColor = .black
// 自定义选中图标颜色
imagePicker?.iconThemeColor = .systemRed
// 自定义相册列表页UI
imagePicker?.albumPickerPageUIConfigBlock = { tableView in
tableView.backgroundColor = .systemBackground
tableView.separatorColor = .systemGray5
}
// 自定义照片选择页UI
imagePicker?.photoPickerPageUIConfigBlock = { collectionView, toolBar, previewBtn, originalBtn, originalLabel, doneBtn, numberImgView, numberLabel, divideLine in
// 自定义底部工具栏
toolBar.backgroundColor = .white
// 自定义预览按钮
previewBtn.setTitleColor(.systemBlue, for: .normal)
previewBtn.setTitleColor(.lightGray, for: .disabled)
// 自定义原图按钮
originalBtn.tintColor = .systemBlue
// 自定义完成按钮
doneBtn.backgroundColor = .systemBlue
doneBtn.layer.cornerRadius = 22
doneBtn.clipsToBounds = true
}
if let picker = imagePicker {
present(picker, animated: true, completion: nil)
}
}
五、常见问题与解决方案
5.1 权限处理
在使用TZImagePickerController之前,最好先检查并请求必要的权限:
import Photos
func checkPhotoPermissions(completion: @escaping (Bool) -> Void) {
let status = PHPhotoLibrary.authorizationStatus(for: .readWrite)
switch status {
case .authorized:
completion(true)
case .denied, .restricted:
// 权限被拒绝,提示用户去设置中开启
showPermissionDeniedAlert()
completion(false)
case .notDetermined:
// 请求权限
PHPhotoLibrary.requestAuthorization(for: .readWrite) { newStatus in
DispatchQueue.main.async {
if newStatus == .authorized {
completion(true)
} else {
self.showPermissionDeniedAlert()
completion(false)
}
}
}
case .limited:
// iOS 14+ 有限权限
completion(true)
@unknown default:
completion(false)
}
}
func showPermissionDeniedAlert() {
let alert = UIAlertController(title: "无法访问相册",
message: "请在设置中允许应用访问相册",
preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "取消", style: .cancel))
alert.addAction(UIAlertAction(title: "设置", style: .default) { _ in
if let settingsURL = URL(string: UIApplication.openSettingsURLString) {
UIApplication.shared.open(settingsURL)
}
})
present(alert, animated: true)
}
// 使用示例
func pickImageWithPermissionCheck() {
checkPhotoPermissions { granted in
if granted {
// 权限已授予,显示图片选择器
self.showImagePicker()
}
}
}
5.2 性能优化
处理大量图片时的性能优化建议:
// 1. 控制图片尺寸,避免加载过大图片
let imagePicker = TZImagePickerController(maxImagesCount: 9, delegate: self)
imagePicker?.photoWidth = 800 // 控制输出图片宽度为800px
imagePicker?.photoPreviewMaxWidth = 1000 // 控制预览图片宽度
// 2. 使用NSOperationQueue控制并发数
let operationQueue = OperationQueue()
operationQueue.maxConcurrentOperationCount = 3 // 限制最大并发数为3
// 3. 获取原图时使用异步方式
func getOriginalImage(for asset: PHAsset, completion: @escaping (UIImage?) -> Void) {
let options = PHImageRequestOptions()
options.isSynchronous = false
options.deliveryMode = .highQualityFormat
options.resizeMode = .none
options.isNetworkAccessAllowed = true // 允许从iCloud下载
let manager = PHImageManager.default()
let targetSize = CGSize(width: asset.pixelWidth, height: asset.pixelHeight)
manager.requestImage(for: asset, targetSize: targetSize, contentMode: .aspectFill, options: options) { image, info in
DispatchQueue.main.async {
completion(image)
}
}
}
// 4. 处理iCloud图片时显示进度提示
func handleICloudAsset(asset: PHAsset) {
let options = PHImageRequestOptions()
options.isNetworkAccessAllowed = true
options.progressHandler = { progress, error, stop, info in
DispatchQueue.main.async {
let progressValue = Float(progress)
// 更新进度指示器
print("加载进度: \(progressValue * 100)%")
}
}
TZImageManager.manager().getPhotoWithAsset(asset, options: options, completion: { image, info, isDegraded in
if let image = image {
print("图片加载完成")
// 显示图片
}
if let info = info, let isCloud = info[PHImageResultIsInCloudKey] as? Bool, isCloud {
if let error = info[PHImageErrorKey] as? NSError {
print("iCloud图片加载失败: \(error.localizedDescription)")
}
}
})
}
5.3 多语言支持
TZImagePickerController内置了多语言支持,可通过以下方式设置:
// 在初始化图片选择器前设置首选语言
let config = TZImagePickerConfig.sharedInstance()
config.preferredLanguage = "zh-Hans" // 简体中文
// config.preferredLanguage = "en" // 英文
// config.preferredLanguage = "ja" // 日文
// config.preferredLanguage = "ko-KP" // 韩文
// config.preferredLanguage = "vi" // 越南语
// 或者直接设置语言bundle
if let bundlePath = Bundle.main.path(forResource: "zh-Hant", ofType: "lproj") {
let languageBundle = Bundle(path: bundlePath)
config.languageBundle = languageBundle
}
// 然后再创建图片选择器
let imagePicker = TZImagePickerController(maxImagesCount: 9, delegate: self)
六、最佳实践与性能优化
6.1 内存管理
图片选择过程中的内存管理最佳实践:
// 1. 使用弱引用避免循环引用
func showImagePicker() {
let imagePicker = TZImagePickerController(maxImagesCount: 9, delegate: self)
imagePicker?.setDidFinishPickingPhotosHandle({ [weak self] photos, assets, isSelectOriginal in
guard let self = self else { return }
self.processSelectedImages(photos: photos, assets: assets)
})
present(imagePicker!, animated: true, completion: nil)
}
// 2. 及时释放大图资源
func processSelectedImages(photos: [UIImage]?, assets: [Any]?) {
// 处理完成后立即释放内存
DispatchQueue.global().async {
autoreleasepool {
// 处理图片的代码
if let photos = photos {
for image in photos {
// 处理每张图片
}
}
// 处理完成后回到主线程更新UI
DispatchQueue.main.async {
// 更新UI
}
}
}
}
// 3. 避免缓存过多图片
let imageCache = NSCache<NSString, UIImage>()
func cacheImage(image: UIImage, key: String) {
// 设置缓存大小限制
imageCache.totalCostLimit = 10 * 1024 * 1024 // 10MB
imageCache.setObject(image, forKey: key as NSString)
}
// 4. 在适当的时候清理缓存
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
imageCache.removeAllObjects()
}
6.2 常见错误处理
处理图片选择过程中可能出现的错误:
// 1. 处理图片加载错误
func handleImageLoadingError(error: Error?, asset: PHAsset) {
if let error = error as NSError? {
switch error.code {
case PHError.cancelled.rawValue:
print("用户取消了图片加载")
case PHError.imageDataUnavailable.rawValue:
print("图片数据不可用")
showErrorAlert(message: "无法加载图片数据")
case PHError.cloudPhotoLibraryUnavailable.rawValue:
print("iCloud照片库不可用")
showErrorAlert(message: "iCloud照片库不可用,请检查网络连接")
case PHError.networkAccessRequired.rawValue:
print("需要网络连接")
showErrorAlert(message: "需要网络连接才能加载此图片")
default:
print("图片加载错误: \(error.localizedDescription)")
showErrorAlert(message: "图片加载失败: \(error.localizedDescription)")
}
}
}
// 2. 显示错误提示
func showErrorAlert(message: String) {
let alert = UIAlertController(title: "错误", message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "确定", style: .default))
present(alert, animated: true)
}
// 3. 处理视频导出错误
func imagePickerController(_ picker: TZImagePickerController!, didFailToSaveEditedVideoWithError error: Error!) {
print("视频保存失败: \(error.localizedDescription)")
showErrorAlert(message: "视频保存失败: \(error.localizedDescription)")
}
七、总结与展望
TZImagePickerController作为一个功能强大的图片选择框架,为iOS开发者提供了丰富的图片和视频选择功能。通过本文的介绍,你已经了解了如何在Swift项目中集成和使用这个框架,包括基础配置、权限处理、功能定制以及性能优化等方面。
7.1 核心功能回顾
- 多选择功能:支持同时选择多张图片和视频
- 原图选择:允许用户选择是否发送原图
- 预览功能:提供图片和视频的预览能力
- 裁剪功能:支持矩形和圆形裁剪,自定义尺寸
- GIF支持:完整支持GIF图片的预览和选择
- 视频编辑:支持视频裁剪和编辑
- UI定制:几乎所有UI元素都可自定义
7.2 未来发展建议
随着iOS系统的不断更新,TZImagePickerController也在持续进化。未来使用中可以关注以下方向:
- Swift重构:虽然目前是Objective-C实现,但可以期待未来的Swift版本
- SwiftUI支持:随着SwiftUI的普及,框架可能会提供SwiftUI接口
- 性能优化:进一步优化大图片处理和iCloud图片加载性能
- AI功能集成:可能会集成图片识别、智能裁剪等AI功能
TZImagePickerController作为一个活跃的开源项目,持续接受社区贡献和改进。建议定期查看项目更新,以获取最新功能和bug修复。
通过合理利用TZImagePickerController,你可以为你的iOS应用快速添加专业级的图片选择功能,提升用户体验,同时减少开发工作量,将更多精力投入到应用的核心功能开发中。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



