TZImagePickerController核心功能解析:如何优雅实现图片/视频多选功能

TZImagePickerController核心功能解析:如何优雅实现图片/视频多选功能

【免费下载链接】TZImagePickerController 一个支持多选、选原图和视频的图片选择器,同时有预览、裁剪功能,支持iOS6+。 A clone of UIImagePickerController, support picking multiple photos、original photo、video, also allow preview photo and video, support iOS6+ 【免费下载链接】TZImagePickerController 项目地址: https://gitcode.com/gh_mirrors/tz/TZImagePickerController

引言:移动端媒体选择的痛点与解决方案

在iOS应用开发中,图片/视频选择功能是社交、电商、内容创作类App的核心需求。然而,系统原生的UIImagePickerController存在诸多限制:不支持多选、缺乏预览功能、无法同时选择图片和视频、UI定制困难等问题。这些痛点直接影响用户体验和开发效率。

TZImagePickerController作为一款功能全面的媒体选择框架,彻底解决了这些问题。它支持图片/视频多选、原图选择、预览、裁剪等核心功能,同时提供高度自定义的UI接口,最低支持iOS10+系统。本文将深入剖析其核心功能实现原理,帮助开发者快速掌握这一强大工具。

读完本文,你将能够:

  • 掌握TZImagePickerController的完整集成流程
  • 实现图片/视频的高效多选功能
  • 理解框架的核心类设计与数据流转机制
  • 定制符合App风格的媒体选择界面
  • 解决性能优化、权限处理等关键问题

一、框架架构与核心类解析

1.1 整体架构设计

TZImagePickerController采用分层设计,主要包含四大模块:资源管理UI控制器数据模型工具类。各模块职责清晰,通过协议通信,保证了代码的可维护性和扩展性。

mermaid

1.2 核心类功能详解

TZImageManager:资源管理中心
  • 功能:封装Photos框架操作,统一管理图片/视频资源的获取、加载和处理
  • 关键方法
    • getAssetFromPHAsset::将系统PHAsset转换为框架内部TZAssetModel
    • requestImageForAsset:size:completion::异步请求指定尺寸的图片
    • getVideoOutputPathWithAsset:completion::获取视频文件路径
  • 性能优化:内部实现请求队列管理,避免并发请求过多导致内存峰值
TZAssetModel:媒体资源模型
  • 属性
    @property (nonatomic, strong) PHAsset *asset;          // 系统资源对象
    @property (nonatomic, assign) TZAssetModelMediaType type; // 资源类型(图片/视频/GIF)
    @property (nonatomic, assign) NSTimeInterval duration; // 视频时长
    @property (nonatomic, assign) BOOL isSelected;        // 是否被选中
    
  • 作用:抽象媒体资源,隔离系统API依赖,便于扩展额外属性
TZImagePickerController:主控制器
  • 核心属性
    @property (nonatomic, assign) NSInteger maxImagesCount; // 最大可选数量
    @property (nonatomic, assign) BOOL allowPickingVideo;   // 是否允许选视频
    @property (nonatomic, assign) BOOL allowPickingOriginalPhoto; // 是否允许选原图
    @property (nonatomic, copy) void (^didFinishPickingPhotosHandle)(NSArray<UIImage *>, NSArray *, BOOL);
    
  • 职责:协调各子控制器,管理选中状态,触发结果回调

二、图片/视频多选功能实现

2.1 多选核心机制

TZImagePickerController的多选功能通过以下机制实现:

  1. 选中状态管理:使用TZAssetModelisSelected属性跟踪选择状态
  2. 数量控制:通过maxImagesCount限制最大选择数量
  3. UI反馈:选中时显示序号标记,达到上限时禁用未选中项
  4. 数据同步:在选择器和预览控制器间共享选中数据

核心实现代码:

// TZAssetCell.m
- (void)setModel:(TZAssetModel *)model {
    _model = model;
    self.selectButton.selected = model.isSelected;
    
    // 更新选中序号
    if (model.isSelected) {
        NSInteger index = [self.tzImagePickerVc.selectedModels indexOfObject:model];
        self.selectIndexButton.hidden = NO;
        self.selectIndexButton.titleLabel.text = [NSString stringWithFormat:@"%zd", index + 1];
    } else {
        self.selectIndexButton.hidden = YES;
    }
    
    // 处理达到最大选择数量的情况
    if (self.tzImagePickerVc.selectedModels.count >= self.tzImagePickerVc.maxImagesCount && !model.isSelected) {
        self.cannotSelectLayer.hidden = NO;
    } else {
        self.cannotSelectLayer.hidden = YES;
    }
}

// TZPhotoPickerController.m
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
    TZAssetModel *model = self.assets[indexPath.item];
    
    // 检查是否可以选择
    if (![self.tzImagePickerVc isAssetCanBeSelected:model.asset]) {
        return;
    }
    
    // 切换选择状态
    if (model.isSelected) {
        [self.tzImagePickerVc removeSelectedModel:model];
    } else {
        if (self.tzImagePickerVc.selectedModels.count >= self.tzImagePickerVc.maxImagesCount) {
            [self showMaxTip];
            return;
        }
        [self.tzImagePickerVc addSelectedModel:model];
    }
    
    // 更新UI
    TZAssetCell *cell = (TZAssetCell *)[collectionView cellForItemAtIndexPath:indexPath];
    cell.model = model;
    [self refreshBottomToolBar];
}

2.2 图片与视频混合选择

TZImagePickerController支持同时选择图片和视频,通过TZAssetModelMediaType枚举区分资源类型:

typedef NS_ENUM(NSInteger, TZAssetModelMediaType) {
    TZAssetModelMediaTypePhoto,    // 图片
    TZAssetModelMediaTypeLivePhoto,// Live Photo
    TZAssetModelMediaTypeVideo,    // 视频
    TZAssetModelMediaTypeAudio     // 音频
};

实现关键点:

  • TZImageManager中根据PHAssetmediaType属性初始化TZAssetModel
  • 在UI层面通过不同图标区分视频和图片(视频显示时长)
  • 在选择逻辑中统一计数,不区分类型(除非特殊配置)

视频特殊处理:

  • 显示时长格式化:01:23(分:秒)
  • 预览时使用专用的视频播放器TZVideoPlayerController
  • 支持视频裁剪功能(通过allowEditVideo属性开启)

2.3 原图选择功能

原图选择是一个重要功能,尤其在需要高质量图片的场景。实现机制如下:

  1. UI层面:底部工具栏提供"原图"开关按钮
  2. 状态管理:通过isSelectOriginalPhoto属性记录选择状态
  3. 数据处理:根据选择状态决定返回压缩图还是原图
// TZImagePickerController.m
- (void)originalPhotoButtonClick:(UIButton *)button {
    button.selected = !button.selected;
    self.isSelectOriginalPhoto = button.selected;
    self.originalPhotoLabel.textColor = button.selected ? self.oKButtonTitleColorNormal : self.barItemTextColor;
}

// 获取图片时根据状态决定质量
- (void)requestImageForAsset:(PHAsset *)asset completion:(void (^)(UIImage *))completion {
    CGSize size = self.isSelectOriginalPhoto ? PHImageManagerMaximumSize : CGSizeMake(828, 828);
    [self.imageManager requestImageForAsset:asset size:size completion:completion];
}

原图选择会显著增加内存占用和传输大小,框架内部通过NSOperationQueue控制并发数,避免内存峰值过高。

三、完整集成与使用指南

3.1 环境配置与依赖

系统要求
  • iOS 10.0+
  • Xcode 10.0+
权限配置

Info.plist中添加必要的权限描述:

<key>NSPhotoLibraryUsageDescription</key>
<string>需要访问您的相册以选择图片</string>
<key>NSCameraUsageDescription</key>
<string>需要访问相机以拍摄照片</string>
<key>NSMicrophoneUsageDescription</key>
<string>需要访问麦克风以录制视频</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>需要访问位置信息以记录照片位置</string>

3.2 安装方法

CocoaPods集成
# Podfile
pod 'TZImagePickerController'        # 完整版本
# 或
pod 'TZImagePickerController/Basic'  # 无定位功能的精简版本

执行安装命令:

pod install
手动集成
  1. 克隆仓库:
git clone https://gitcode.com/gh_mirrors/tz/TZImagePickerController.git
  1. TZImagePickerController文件夹拖拽到项目中
  2. 添加依赖框架:Photos.frameworkAVFoundation.framework

3.3 基础使用示例

1. 简单多选功能
#import "TZImagePickerController.h"

// 在需要调用的地方
- (void)showImagePicker {
    // 初始化选择器,最多选择9张图片
    TZImagePickerController *imagePicker = [[TZImagePickerController alloc] initWithMaxImagesCount:9 delegate:self];
    
    // 设置代理回调
    [imagePicker setDidFinishPickingPhotosHandle:^(NSArray<UIImage *> *photos, NSArray *assets, BOOL isSelectOriginalPhoto) {
        // 处理选中的图片
        self.selectedPhotos = photos;
        [self.collectionView reloadData];
    }];
    
    // 模态弹出
    [self presentViewController:imagePicker animated:YES completion:nil];
}

// 实现代理方法(可选)
#pragma mark - TZImagePickerControllerDelegate
- (void)imagePickerController:(TZImagePickerController *)picker didFinishPickingPhotos:(NSArray<UIImage *> *)photos sourceAssets:(NSArray *)assets isSelectOriginalPhoto:(BOOL)isSelectOriginalPhoto {
    NSLog(@"选中了%zd张图片,是否原图:%@", photos.count, isSelectOriginalPhoto ? @"是" : @"否");
    [picker dismissViewControllerAnimated:YES completion:nil];
}
2. 高级配置示例
// 配置同时选择图片和视频
TZImagePickerController *imagePicker = [[TZImagePickerController alloc] initWithMaxImagesCount:9 delegate:self];
imagePicker.allowPickingVideo = YES;               // 允许选择视频
imagePicker.allowPickingImage = YES;               // 允许选择图片
imagePicker.allowPickingGif = YES;                 // 允许选择GIF
imagePicker.allowEditVideo = YES;                  // 允许编辑视频
imagePicker.maxCropVideoDuration = 15;             // 视频最大裁剪时长15秒
imagePicker.allowPreview = YES;                    // 允许预览
imagePicker.showSelectedIndex = YES;               // 显示选中序号
imagePicker.photoWidth = 1200;                     // 导出图片宽度
imagePicker.oKButtonTitleColorNormal = [UIColor redColor]; // 自定义按钮颜色
3. 单选裁剪功能
// 初始化裁剪模式的选择器
TZImagePickerController *imagePicker = [[TZImagePickerController alloc] initWithMaxImagesCount:1 delegate:self];
imagePicker.allowCrop = YES;                       // 允许裁剪
imagePicker.cropRect = CGRectMake(0, 100, 300, 300); // 设置裁剪框
imagePicker.needCircleCrop = YES;                  // 圆形裁剪
imagePicker.circleCropRadius = 150;                // 圆形裁剪半径

// 裁剪完成回调
imagePicker.didFinishPickingPhotosHandle = ^(NSArray<UIImage *> *photos, NSArray *assets, BOOL isSelectOriginalPhoto) {
    self.avatarImageView.image = photos.firstObject;
};

3.4 自定义UI样式

TZImagePickerController提供丰富的UI自定义接口,几乎所有可见元素都可以定制:

// 自定义导航栏样式
imagePicker.naviBgColor = [UIColor whiteColor];
imagePicker.naviTitleColor = [UIColor blackColor];
imagePicker.naviTitleFont = [UIFont boldSystemFontOfSize:18];

// 自定义底部工具栏
imagePicker.photoPickerPageUIConfigBlock = ^(UICollectionView *collectionView, UIView *bottomToolBar, UIButton *previewButton, UIButton *originalPhotoButton, UILabel *originalPhotoLabel, UIButton *doneButton, UIImageView *numberImageView, UILabel *numberLabel, UIView *divideLine) {
    // 修改完成按钮样式
    doneButton.backgroundColor = [UIColor blueColor];
    doneButton.layer.cornerRadius = 22;
    doneButton.clipsToBounds = YES;
    
    // 修改分割线
    divideLine.backgroundColor = [UIColor lightGrayColor];
};

// 自定义选中图标
imagePicker.photoSelImage = [UIImage imageNamed:@"custom_selected"];
imagePicker.photoDefImage = [UIImage imageNamed:@"custom_unselected"];

四、性能优化与最佳实践

4.1 性能优化策略

TZImagePickerController在设计时充分考虑了性能问题,采用多种优化策略:

图片加载优化
  • 按需加载:只加载当前可见区域的图片
  • 尺寸适配:根据显示需求请求合适尺寸的图片,避免大图片占用过多内存
  • 缓存机制:利用Photos框架的缓存机制,避免重复请求
// 高效图片加载
- (void)requestImageForAsset:(PHAsset *)asset size:(CGSize)size completion:(void (^)(UIImage *image))completion {
    PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
    options.resizeMode = PHImageRequestOptionsResizeModeFast;
    options.deliveryMode = PHImageRequestOptionsDeliveryModeOpportunistic;
    options.synchronous = NO;
    
    [self.imageManager requestImageForAsset:asset targetSize:size contentMode:PHImageContentModeAspectFill options:options resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
        BOOL downloadFinined = ![[info objectForKey:PHImageCancelledKey] boolValue] && ![info objectForKey:PHImageErrorKey];
        if (result && downloadFinined) {
            completion(result);
        }
    }];
}
内存管理
  • 自动释放:及时释放不可见的图片资源
  • 并发控制:限制同时请求的图片数量
  • 大列表优化:采用UICollectionView的重用机制
视频处理优化
  • 异步处理:视频导出和处理在后台线程进行
  • 进度反馈:提供导出进度提示,提升用户体验
  • 临时文件管理:及时清理不再需要的临时视频文件

4.2 常见问题解决方案

问题1:多选大量图片时内存飙升

解决方案

  • 限制最大可选数量(建议不超过20张)
  • 使用onlyReturnAsset模式,只返回PHAsset对象,需要时再加载图片
  • 实现图片加载队列控制
// 优化大量图片选择
imagePicker.onlyReturnAsset = YES; // 只返回PHAsset,不立即加载图片

// 需要显示图片时再单独请求
- (void)loadImageForAsset:(PHAsset *)asset {
    [[TZImageManager sharedManager] requestImageForAsset:asset size:CGSizeMake(200, 200) completion:^(UIImage *image) {
        // 更新UI
    }];
}
问题2:iOS14+权限适配

解决方案: iOS14引入了"Limited Photos Library"权限,需要特殊处理:

// 检查并请求完整相册访问权限
- (void)checkPhotoLibraryPermission {
    if (@available(iOS 14, *)) {
        [PHPhotoLibrary requestAuthorizationForAccessLevel:PHAccessLevelRead handler:^(PHAuthorizationStatus status) {
            if (status == PHAuthorizationStatusLimited) {
                // 提示用户授予完整访问权限
                dispatch_async(dispatch_get_main_queue(), ^{
                    [self showLimitedAccessAlert];
                });
            }
        }];
    }
}

// 提示用户切换到完整访问权限
- (void)showLimitedAccessAlert {
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"提示" message:@"当前仅能访问部分照片,请到设置中授予完整相册访问权限" preferredStyle:UIAlertControllerStyleAlert];
    [alert addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil]];
    [alert addAction:[UIAlertAction actionWithTitle:@"去设置" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString] options:@{} completionHandler:nil];
    }]];
    [self presentViewController:alert animated:YES completion:nil];
}
问题3:自定义UI后布局错乱

解决方案

  • 使用框架提供的布局回调进行自定义
  • 注意适配不同屏幕尺寸和横竖屏切换
  • 避免直接修改框架内部视图的frame
// 正确的布局自定义方式
imagePicker.photoPickerPageDidLayoutSubviewsBlock = ^(UICollectionView *collectionView, UIView *bottomToolBar, UIButton *previewButton, UIButton *originalPhotoButton, UILabel *originalPhotoLabel, UIButton *doneButton, UIImageView *numberImageView, UILabel *numberLabel, UIView *divideLine) {
    // 适配安全区域
    CGFloat bottomInset = [TZCommonTools tz_safeAreaInsets].bottom;
    bottomToolBar.frame = CGRectMake(0, [UIScreen mainScreen].bounds.size.height - 49 - bottomInset, [UIScreen mainScreen].bounds.size.width, 49 + bottomInset);
    
    // 重新布局按钮位置
    doneButton.frame = CGRectMake([UIScreen mainScreen].bounds.size.width - 15 - 80, 7, 80, 35);
};

4.3 最佳实践总结

  1. 权限处理

    • 提前请求权限,避免使用时才弹窗
    • 处理权限被拒绝的情况,提供友好提示
  2. 性能优化

    • 根据实际需求设置合理的maxImagesCount
    • 列表滑动时暂停图片加载,停止滑动后恢复
    • 大图预览时注意内存释放
  3. 用户体验

    • 提供清晰的选择状态反馈
    • 操作耗时较长时显示加载指示器
    • 支持横屏模式,提升iPad体验
  4. 兼容性处理

    • 适配不同iOS版本的特性差异
    • 测试各种权限组合场景
    • 处理特殊资源(如GIF、Live Photo、iCloud照片)

五、总结与展望

TZImagePickerController作为一款成熟的媒体选择框架,凭借其全面的功能、良好的性能和高度的可定制性,成为iOS开发中媒体选择功能的首选解决方案。它不仅解决了原生组件的功能限制,还提供了丰富的扩展接口,满足各种定制需求。

主要优势回顾

  • 功能全面:支持图片/视频多选、预览、裁剪、原图选择等核心功能
  • 易于集成:提供多种集成方式,API设计简洁直观
  • 高度可定制:几乎所有UI元素都可以自定义,满足不同App风格需求
  • 性能优化:针对图片加载、内存管理等关键问题进行专项优化
  • 持续维护:活跃的社区支持和版本更新,及时适配新系统

未来发展方向

  • Swift版本迁移:目前框架主要使用Objective-C编写,未来可能提供Swift版本
  • SwiftUI支持:提供SwiftUI组件,适应iOS开发新趋势
  • 功能扩展:增加图片编辑、滤镜等高级功能
  • ARKit集成:结合AR技术提供更丰富的媒体交互体验

通过本文的介绍,相信开发者已经对TZImagePickerController有了深入了解。无论是快速集成基本功能,还是深度定制满足特殊需求,这款框架都能提供有力支持。建议开发者结合实际项目需求,充分利用框架提供的各项功能和扩展点,打造出色的媒体选择体验。

最后,感谢框架作者谭真(banchichen)的辛勤付出,以及社区贡献者的持续优化,让iOS开发者拥有如此强大的媒体选择工具。

【免费下载链接】TZImagePickerController 一个支持多选、选原图和视频的图片选择器,同时有预览、裁剪功能,支持iOS6+。 A clone of UIImagePickerController, support picking multiple photos、original photo、video, also allow preview photo and video, support iOS6+ 【免费下载链接】TZImagePickerController 项目地址: https://gitcode.com/gh_mirrors/tz/TZImagePickerController

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值