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

引言:为什么模块化对图片选择器至关重要

你是否曾面临图片选择器代码混乱、耦合严重、难以维护的问题?当需要添加新功能时,是否因为牵一发而动全身的代码结构而束手无策?TZImagePickerController作为一款功能强大的iOS图片选择框架,通过精心设计的模块化架构,完美解决了这些痛点。本文将深入剖析TZImagePickerController的模块化设计理念,展示其如何实现组件解耦与复用,帮助开发者构建更灵活、可扩展的图片选择功能。

读完本文,你将获得:

  • 理解TZImagePickerController的核心模块划分原则
  • 掌握组件间通信与协作的关键机制
  • 学习如何基于现有模块快速扩展新功能
  • 了解模块化设计在实际项目中的最佳实践

一、模块化架构概览

TZImagePickerController采用分层设计与功能模块化相结合的方式,将复杂的图片选择功能拆解为多个职责单一、接口清晰的独立组件。这种架构不仅提升了代码的可维护性,还极大增强了功能的可扩展性。

1.1 核心模块划分

通过对TZImagePickerController头文件的分析,我们可以识别出以下核心模块:

mermaid

1.2 模块间依赖关系

各模块之间通过明确定义的接口进行通信,形成了清晰的依赖层次:

mermaid

二、核心模块详解

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负责管理不同功能页面之间的导航逻辑,如从相册列表到照片预览,从预览到编辑等:

mermaid

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是相册中单个图片/视频项的展示单元,封装了图片展示、选择状态、媒体类型标识等功能:

mermaid

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];
扩展自定义功能

基于现有模块,我们可以轻松扩展新功能。例如,添加图片水印功能:

  1. 创建TZImageWatermarkManager工具类处理水印逻辑
  2. 扩展TZImageManager,添加带水印的图片获取方法
  3. 在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通过精心的模块化设计,将复杂的图片选择功能分解为职责单一、接口清晰的模块,实现了代码的高内聚低耦合。主要特点包括:

  1. 清晰的模块划分:按功能将系统分为控制器、数据管理、视图组件和工具类四大类模块
  2. 明确的接口定义:每个模块通过头文件暴露清晰的接口,隐藏实现细节
  3. 灵活的配置选项:通过属性和协议提供丰富的自定义选项
  4. 复用性强的组件:视图组件和工具类可在不同场景中复用

4.2 模块化带来的收益

  • 可维护性提升:模块职责单一,代码边界清晰,便于定位和修复问题
  • 可扩展性增强:新功能可以通过添加模块或扩展现有模块实现,不影响其他部分
  • 团队协作高效:不同开发者可以并行开发不同模块,减少代码冲突
  • 测试便捷性:单个模块可以独立进行单元测试,提高测试效率

4.3 未来优化方向

尽管TZImagePickerController的模块化设计已经相当成熟,但仍有一些可以优化的方向:

  1. 引入依赖注入:通过依赖注入进一步解耦模块间依赖,提高测试性
  2. 响应式编程:考虑引入RxSwift或Combine,优化异步操作和事件处理
  3. 组件化升级:将核心模块进一步拆分为独立组件,支持CocoaPods或Swift Package Manager单独分发
  4. Swift重构:逐步将Objective-C代码重构为Swift,利用Swift的现代特性提升代码安全性和简洁性

TZImagePickerController的模块化设计为iOS图片选择器树立了良好的架构典范,其设计思想和实现方式值得在其他复杂UI组件开发中借鉴和应用。通过不断优化和演进,模块化架构将继续为框架的稳定性和扩展性提供坚实基础。

【免费下载链接】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、付费专栏及课程。

余额充值