TZImagePickerController模块化设计:组件解耦与复用
引言:为什么模块化对图片选择器至关重要
你是否曾面临图片选择器代码混乱、耦合严重、难以维护的问题?当需要添加新功能时,是否因为牵一发而动全身的代码结构而束手无策?TZImagePickerController作为一款功能强大的iOS图片选择框架,通过精心设计的模块化架构,完美解决了这些痛点。本文将深入剖析TZImagePickerController的模块化设计理念,展示其如何实现组件解耦与复用,帮助开发者构建更灵活、可扩展的图片选择功能。
读完本文,你将获得:
- 理解TZImagePickerController的核心模块划分原则
- 掌握组件间通信与协作的关键机制
- 学习如何基于现有模块快速扩展新功能
- 了解模块化设计在实际项目中的最佳实践
一、模块化架构概览
TZImagePickerController采用分层设计与功能模块化相结合的方式,将复杂的图片选择功能拆解为多个职责单一、接口清晰的独立组件。这种架构不仅提升了代码的可维护性,还极大增强了功能的可扩展性。
1.1 核心模块划分
通过对TZImagePickerController头文件的分析,我们可以识别出以下核心模块:
1.2 模块间依赖关系
各模块之间通过明确定义的接口进行通信,形成了清晰的依赖层次:
二、核心模块详解
2.1 控制器模块:流程导航与用户交互
TZImagePickerController:中央协调者
TZImagePickerController作为整个框架的入口点,扮演着中央协调者的角色。它定义了一系列初始化方法,支持不同的使用场景:
// 标准初始化方法
- (instancetype)initWithMaxImagesCount:(NSInteger)maxImagesCount
delegate:(id<TZImagePickerControllerDelegate>)delegate;
// 仅用于预览图片的初始化方法
- (instancetype)initWithSelectedAssets:(NSMutableArray *)selectedAssets
selectedPhotos:(NSMutableArray *)selectedPhotos
index:(NSInteger)index;
// 用于图片裁剪的初始化方法
- (instancetype)initCropTypeWithAsset:(PHAsset *)asset
photo:(UIImage *)photo
completion:(void (^)(UIImage *cropImage,PHAsset *asset))completion;
该类通过属性配置提供了丰富的自定义选项,允许开发者根据需求灵活调整选择器行为:
// 选择限制
@property (nonatomic, assign) NSInteger maxImagesCount; // 默认9张
@property (nonatomic, assign) NSInteger minImagesCount; // 默认0张
// 功能开关
@property (nonatomic, assign) BOOL allowPickingOriginalPhoto; // 允许选原图
@property (nonatomic, assign) BOOL allowPickingVideo; // 允许选视频
@property (nonatomic, assign) BOOL allowEditVideo; // 允许编辑视频
@property (nonatomic, assign) BOOL allowPickingGif; // 允许选GIF
// UI自定义
@property (nonatomic, strong) UIColor *naviBgColor; // 导航栏背景色
@property (nonatomic, strong) UIColor *naviTitleColor; // 导航栏标题色
@property (nonatomic, strong) UIFont *naviTitleFont; // 导航栏标题字体
通过定义TZImagePickerControllerDelegate协议,实现了与外部的解耦通信:
@protocol TZImagePickerControllerDelegate <NSObject>
@optional
- (void)imagePickerController:(TZImagePickerController *)picker
didFinishPickingPhotos:(NSArray<UIImage *> *)photos
sourceAssets:(NSArray *)assets
isSelectOriginalPhoto:(BOOL)isSelectOriginalPhoto;
- (void)imagePickerController:(TZImagePickerController *)picker
didFinishPickingVideo:(UIImage *)coverImage
sourceAssets:(PHAsset *)asset;
- (void)tz_imagePickerControllerDidCancel:(TZImagePickerController *)picker;
@end
页面导航与控制器协作
TZImagePickerController负责管理不同功能页面之间的导航逻辑,如从相册列表到照片预览,从预览到编辑等:
2.2 数据管理层:资产获取与状态管理
TZImageManager:图片资源管理中心
TZImageManager是数据管理层的核心,负责与系统Photos框架交互,统一处理图片资源的获取、缓存和处理:
@interface TZImageManager : NSObject
+ (instancetype)manager NS_SWIFT_NAME(default());
// 授权检查
- (BOOL)authorizationStatusAuthorized;
- (void)requestAuthorizationWithCompletion:(void (^)(void))completion;
// 相册获取
- (void)getCameraRollAlbumWithFetchAssets:(BOOL)needFetchAssets
completion:(void (^)(TZAlbumModel *model))completion;
- (void)getAllAlbumsWithFetchAssets:(BOOL)needFetchAssets
completion:(void (^)(NSArray<TZAlbumModel *> *models))completion;
// 图片获取
- (PHImageRequestID)getPhotoWithAsset:(PHAsset *)asset
completion:(void (^)(UIImage *photo, NSDictionary *info, BOOL isDegraded))completion;
- (PHImageRequestID)getOriginalPhotoWithAsset:(PHAsset *)asset
completion:(void (^)(UIImage *photo, NSDictionary *info))completion;
// 视频获取
- (void)getVideoWithAsset:(PHAsset *)asset
completion:(void (^)(AVPlayerItem * playerItem, NSDictionary * info))completion;
@end
TZImageManager采用单例模式设计,确保全局只有一个实例负责资源管理,避免了重复请求和缓存混乱:
+ (instancetype)manager {
static TZImageManager *instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[TZImageManager alloc] init];
instance.cachingImageManager = [[PHCachingImageManager alloc] init];
// 默认配置
instance.photoPreviewMaxWidth = 600;
instance.photoWidth = 828;
instance.sortAscendingByModificationDate = YES;
});
return instance;
}
TZAssetModel:统一数据模型
TZAssetModel封装了PHAsset及相关元数据,为上层提供统一的数据访问接口:
typedef enum : NSUInteger {
TZAssetModelMediaTypePhoto = 0, // 静态图片
TZAssetModelMediaTypeLivePhoto, // Live Photo
TZAssetModelMediaTypePhotoGif, // GIF图片
TZAssetModelMediaTypeVideo, // 视频
TZAssetModelMediaTypeAudio // 音频
} TZAssetModelMediaType;
@interface TZAssetModel : NSObject
@property (nonatomic, strong) PHAsset *asset; // 系统资产对象
@property (nonatomic, assign) BOOL isSelected; // 选中状态
@property (nonatomic, assign) TZAssetModelMediaType type; // 媒体类型
@property (nonatomic, copy) NSString *timeLength; // 时长(视频)
@property (nonatomic, assign) BOOL iCloudFailed; // iCloud加载失败标记
+ (instancetype)modelWithAsset:(PHAsset *)asset type:(TZAssetModelMediaType)type;
@end
通过TZAssetModel,上层模块无需直接处理PHAsset的复杂性,统一了不同类型媒体资源的访问方式,降低了耦合度。
2.3 视图组件:UI元素的封装与复用
TZAssetCell:图片展示与选择
TZAssetCell是相册中单个图片/视频项的展示单元,封装了图片展示、选择状态、媒体类型标识等功能:
TZAssetCell通过setModel:方法实现与数据模型的绑定,内部处理不同媒体类型的展示逻辑:
- (void)setModel:(TZAssetModel *)model {
_model = model;
// 根据媒体类型显示不同图标
if (model.type == TZAssetModelMediaTypeVideo) {
self.videoImgView.hidden = NO;
self.timeLength.hidden = NO;
self.timeLength.text = model.timeLength;
} else if (model.type == TZAssetModelMediaTypePhotoGif) {
self.videoImgView.hidden = YES;
self.timeLength.hidden = NO;
self.timeLength.text = @"GIF";
} else {
self.videoImgView.hidden = YES;
self.timeLength.hidden = YES;
}
// 加载图片
[[TZImageManager manager] getPhotoWithAsset:model.asset
completion:^(UIImage *photo, NSDictionary *info, BOOL isDegraded) {
self.imageView.image = photo;
}];
// 设置选中状态
self.selected = model.isSelected;
}
TZProgressView:加载进度指示器
TZProgressView是一个轻量级的进度指示器组件,用于显示图片加载或视频处理的进度:
@interface TZProgressView : UIView
@property (nonatomic, assign) CGFloat progress;
@property (nonatomic, strong) UIColor *progressColor; // 进度条颜色
@property (nonatomic, strong) UIColor *trackColor; // 轨道颜色
@end
通过将进度指示功能封装为独立组件,使得在图片预览、视频加载等多个场景中可以方便地复用,保证了UI风格的一致性,同时简化了进度更新逻辑。
2.4 工具类:通用功能的抽象
UIView+TZLayout:AutoLayout简化工具
为简化UI布局代码,TZImagePickerController提供了UIView的分类UIView+TZLayout,封装了常用的AutoLayout操作:
@interface UIView (TZLayout)
// 快捷设置frame
@property (nonatomic, assign) CGFloat tz_x;
@property (nonatomic, assign) CGFloat tz_y;
@property (nonatomic, assign) CGFloat tz_width;
@property (nonatomic, assign) CGFloat tz_height;
@property (nonatomic, assign) CGSize tz_size;
@property (nonatomic, assign) CGPoint tz_origin;
// 快捷添加约束
- (void)tz_alignAxisToSuperviewAxis:(NSLayoutAttribute)axis;
- (void)tz_alignAxis:(NSLayoutAttribute)axis toView:(UIView *)view axis:(NSLayoutAttribute)toAxis;
- (void)tz_constrainAttribute:(NSLayoutAttribute)attribute toView:(UIView *)view attribute:(NSLayoutAttribute)toAttribute;
- (void)tz_constrainAttributes:(NSArray *)attributes toView:(UIView *)view attributes:(NSArray *)toAttributes;
@end
使用这些分类方法,可以大幅简化布局代码:
// 传统AutoLayout代码
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self.imageView
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.contentView
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0];
[self.contentView addConstraint:constraint];
// 使用UIView+TZLayout
[self.imageView tz_alignAxisToSuperviewAxis:NSLayoutAttributeCenterX];
NSBundle+TZImagePicker:资源管理
为支持多语言和资源本地化,TZImagePickerController封装了NSBundle分类,简化了资源访问:
@interface NSBundle (TZImagePicker)
+ (NSBundle *)tz_imagePickerBundle;
+ (NSString *)tz_localizedStringForKey:(NSString *)key;
+ (NSString *)tz_localizedStringForKey:(NSString *)key value:(NSString *)value;
@end
通过这个分类,框架内部可以统一访问资源文件,无需关心具体的语言版本,系统会根据当前语言设置自动选择合适的资源:
// 获取本地化字符串
NSString *cancelTitle = [NSBundle tz_localizedStringForKey:@"Cancel"];
// 获取图片资源
UIImage *image = [UIImage imageNamedFromMyBundle:@"photo_sel"];
三、模块化设计的优势与最佳实践
3.1 模块解耦带来的好处
关注点分离
TZImagePickerController的模块化设计使每个模块专注于单一职责:
- 控制器模块专注于流程控制和用户交互
- 数据管理层专注于资源获取和状态管理
- 视图组件专注于UI展示和交互反馈
- 工具类提供通用功能支持
这种分离使得代码更加清晰,每个模块的职责明确,便于理解和维护。
可替换性与可扩展性
模块化设计使得替换或扩展某个功能变得简单。例如,如果需要更换图片加载方式,只需修改TZImageManager的实现,而无需改动使用它的控制器或视图组件。
假设我们需要添加对网络图片的支持,只需扩展TZImageManager:
// TZImageManager.h
@property (nonatomic, assign) BOOL isPreviewNetworkImage; // 新增属性
// TZImageManager.m
- (PHImageRequestID)getPhotoWithAsset:(PHAsset *)asset
completion:(void (^)(UIImage *photo, NSDictionary *info, BOOL isDegraded))completion {
if (self.isPreviewNetworkImage && [asset isKindOfClass:[TZNetworkAsset class]]) {
// 网络图片加载逻辑
[self loadNetworkImageWithAsset:(TZNetworkAsset *)asset completion:completion];
return PHInvalidImageRequestID;
} else {
// 原有本地图片加载逻辑
return [self getPhotoWithAsset:asset photoWidth:self.photoWidth completion:completion];
}
}
这种扩展方式不会影响到已有的使用TZImageManager的代码,体现了开闭原则。
3.2 模块间通信机制
TZImagePickerController各模块之间通过以下几种方式进行通信,确保模块间低耦合:
协议委托(Delegate)
控制器模块之间主要通过协议委托进行通信,如TZImagePickerControllerDelegate定义了选择完成、取消等事件的回调接口。
通知(Notification)
对于跨模块的事件通知,TZImagePickerController使用系统通知机制,如语言切换、主题变化等全局事件:
// 发送通知
[[NSNotificationCenter defaultCenter] postNotificationName:kTZImagePickerLanguageChangedNotification object:nil];
// 接收通知
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(languageChanged:)
name:kTZImagePickerLanguageChangedNotification
object:nil];
回调Block
在需要即时响应的场景,如图片加载完成、视频处理进度等,使用Block回调:
// TZImageManager.h
- (PHImageRequestID)getPhotoWithAsset:(PHAsset *)asset
completion:(void (^)(UIImage *photo, NSDictionary *info, BOOL isDegraded))completion;
// 使用示例
[[TZImageManager manager] getPhotoWithAsset:asset
completion:^(UIImage *photo, NSDictionary *info, BOOL isDegraded) {
self.imageView.image = photo;
if (!isDegraded) {
// 图片加载完成,隐藏进度指示器
self.progressView.hidden = YES;
}
}];
3.3 实际应用中的最佳实践
按需定制选择器功能
利用TZImagePickerController的模块化设计,我们可以根据实际需求灵活配置功能:
// 创建一个只允许选择视频的选择器
TZImagePickerController *picker = [[TZImagePickerController alloc] initWithMaxImagesCount:1 delegate:self];
picker.allowPickingImage = NO; // 禁用图片选择
picker.allowPickingVideo = YES; // 启用视频选择
picker.allowEditVideo = YES; // 允许编辑视频
picker.videoMaximumDuration = 60; // 最长1分钟视频
picker.presetName = AVAssetExportPresetMediumQuality; // 视频质量
// 自定义导航栏样式
picker.naviBgColor = [UIColor blackColor];
picker.naviTitleColor = [UIColor whiteColor];
picker.barItemTextColor = [UIColor whiteColor];
[self presentViewController:picker animated:YES completion:nil];
扩展自定义功能
基于现有模块,我们可以轻松扩展新功能。例如,添加图片水印功能:
- 创建TZImageWatermarkManager工具类处理水印逻辑
- 扩展TZImageManager,添加带水印的图片获取方法
- 在TZPhotoPreviewController中添加水印开关
// TZImageWatermarkManager.h
@interface TZImageWatermarkManager : NSObject
+ (instancetype)sharedManager;
- (UIImage *)imageWithWatermark:(UIImage *)image;
@property (nonatomic, copy) NSString *watermarkText; // 水印文字
@property (nonatomic, strong) UIFont *watermarkFont; // 水印字体
@property (nonatomic, strong) UIColor *watermarkColor; // 水印颜色
@end
// TZImageManager+Watermark.h
@interface TZImageManager (Watermark)
- (PHImageRequestID)getPhotoWithAsset:(PHAsset *)asset
addWatermark:(BOOL)addWatermark
completion:(void (^)(UIImage *photo, NSDictionary *info, BOOL isDegraded))completion;
@end
这种扩展方式充分利用了原有的模块化结构,保持了代码的整洁和可维护性。
四、总结与展望
4.1 模块化设计回顾
TZImagePickerController通过精心的模块化设计,将复杂的图片选择功能分解为职责单一、接口清晰的模块,实现了代码的高内聚低耦合。主要特点包括:
- 清晰的模块划分:按功能将系统分为控制器、数据管理、视图组件和工具类四大类模块
- 明确的接口定义:每个模块通过头文件暴露清晰的接口,隐藏实现细节
- 灵活的配置选项:通过属性和协议提供丰富的自定义选项
- 复用性强的组件:视图组件和工具类可在不同场景中复用
4.2 模块化带来的收益
- 可维护性提升:模块职责单一,代码边界清晰,便于定位和修复问题
- 可扩展性增强:新功能可以通过添加模块或扩展现有模块实现,不影响其他部分
- 团队协作高效:不同开发者可以并行开发不同模块,减少代码冲突
- 测试便捷性:单个模块可以独立进行单元测试,提高测试效率
4.3 未来优化方向
尽管TZImagePickerController的模块化设计已经相当成熟,但仍有一些可以优化的方向:
- 引入依赖注入:通过依赖注入进一步解耦模块间依赖,提高测试性
- 响应式编程:考虑引入RxSwift或Combine,优化异步操作和事件处理
- 组件化升级:将核心模块进一步拆分为独立组件,支持CocoaPods或Swift Package Manager单独分发
- Swift重构:逐步将Objective-C代码重构为Swift,利用Swift的现代特性提升代码安全性和简洁性
TZImagePickerController的模块化设计为iOS图片选择器树立了良好的架构典范,其设计思想和实现方式值得在其他复杂UI组件开发中借鉴和应用。通过不断优化和演进,模块化架构将继续为框架的稳定性和扩展性提供坚实基础。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



