MDModuleManager
MDModuleManager依赖:
1.协议默认实现MDProtocolImplementation 查看介绍
2.面向切面Aspects 查看介绍
ModuleManager是一款处理界面模块化的架构。他具备高度封装、业务逻辑高度分离等特性,还能使得界面间跳转逻辑单独分离,从而达到界面功能高度复用,无需为流程的改动、增减而重写界面代码的效果。
MDModuleManager 与传统的MVC框架完全兼容,可以灵活选择是否使用MDModuleManager
MDModuleManager 不是一个基类,而是一个协议。并且使用MDProtocolImplementation 提供了协议的默认实现。因此用户可以用任何类,作为MDModuleManager。
ModuleManager 可以被推入(Push),也可以作为一个整体被弹出(Pop)。推入ModuleManager的角色,无需关心Module中的界面,直接push ModuleManager,或者push ModuleManager.rootViewController即可。ModuleManager作为一个整体,也可以调用其popAll方法,来弹出该模块所有界面。当然popAll是有限制的,只有当模块所管理的界面栈的最后一个界面,也是当前NavigationController所管理的界面栈的最后一个界面时,才可以popAll。
一个父ModuleManager可以推入一个子ModuleManager。如果父ModuleManager符合popAll条件,可以直接调用父ModuleManager的popAll,来pop掉所有界面,此时子ModuleManager界面也会被弹出。
在传统的MVC框架中,当我们从模块A的某个界面a1,跳转到模块B的时候,有时候需要根据业务B的数据情况,跳转不同的界面,作为模块B的初始界面。这时候不可避免的要在界面a1去获取这些业务B的数据。而ModuleManager解决了这个问题。步骤如下:
1.而ModuleManager提供了预处理方法如下,模块B重写此方法,获取数据,记录在模块B的属性中,最后回调finish()。
- (void)preprocessWithFinish:(void (NS_NOESCAPE ^)(id error))finish;
2.当a1在需要push模块B之前,首先调用此方法,在finish回调中,去push模块B。此时框架会自动调用模块B的 generateRootViewController 方法生成第一个界面。
3.在模块B重写 generateRootViewController 方法时,根据之前记录的数据,生成不同的界面。
1.可以直接在AppDelegate中生成一个ModuleManager,然后用它来管理整个App的界面流。
2.可以直接利用navigationController,来push ModuleManager.rootViewController。来推入一个模块。
3.可以使用[MDModuleManager pushSubModuleManager: animated:]方法,来推入一个子模块
PS:由于ModuleManager的兼容性,以上三中使用方式,可以任意选择使用或者不使用
//MDMMImageModuleManager.h
#import <Foundation/Foundation.h>
#import "MDModuleManager.h"
NS_ASSUME_NONNULL_BEGIN
@interface MDMMImageModuleManager : NSObject <MDModuleManager>
@end
NS_ASSUME_NONNULL_END
1.需要在.m文件中使用*ImplementationProtocol* 来添加的默认实现
2.需要重写 *- (UIViewController )generateRootViewController 方法来生成模块的第一个界面。此方法会在首次调用ModuleManager.rootViewController时调用,不会重复调用。如果不重写此方法,则会默认创建一个UIViewController的实例,而这样往往是没有任何实际用处的。
//MDMMImageModuleManager.m
#import "MDMMImageModuleManager.h"
#import "MDMMImageHomeViewController.h"
@implementation MDMMImageModuleManager
__ImplementationProtocol__
- (UIViewController *)generateRootViewController {
return [[MDMMImageHomeViewController alloc] init];
}
@end
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] init];
MDMMImageModuleManager *homeModuleManager = [[MDMMImageModuleManager alloc] initWithNavigationController:nil];
self.window.rootViewController = homeModuleManager.navigationController;
[self.window makeKeyAndVisible];
return YES;
}
- (void)viewDidLoad {
//...
[self.button addTarget:self action:@selector(push) forControlEvents:UIControlEventTouchUpInside];
//...
}
- (void)push {
MDMMImageProcess1 *mm = [[MDMMImageProcess1 alloc] initWithImage:self.imageView.image navigationController:self.navigationController];
[self.navigationController pushViewController:mm.rootViewController animated:YES];
}
- (void)viewDidLoad {
//...
[self.button1 addTarget:self action:@selector(process1) forControlEvents:UIControlEventTouchUpInside];
//...
}
- (void)process1 {
MDMMImageProcess1 *mm = [[MDMMImageProcess1 alloc] initWithImage:self.imageView.image navigationController:self.navigationController];
[self.moduleManager pushSubModuleManager:mm animated:YES];
//...
}
//MDMMImageModuleManager.m
@implementation MDMMImageModuleManager
__ImplementationProtocol__
//...
- (void)pushViewController {
UIViewController *viewController = [[UIViewController alloc] init];
[self pushViewController:viewController animated:YES]
}
//...
@end
后面我们将以图像处理Demo,作为范例,来进一步演示这个框架。
图像处理Demo的所有代码,在Demo工程中的 **MDToolsDemo/MDModuleManagerDemo_image** 文件夹下,运行时需要将main.m文件中的AppDelegate修改为 **DemoAppDelegate_image** 类。
用户可以在首页选择一张图片显示在首页上,然后可以选择三种图片编辑流程来编辑图片。无论选择哪种处理流程,在处理完毕后,所有处理流程中的功能界面,都会被pop掉,回到首页。然后需要把处理完的图片显示在首页上。
1.Demo共四个界面:首页、图片裁剪页面、图片滤镜、图片加水印
2.首页:提供四个按钮:选择照片按钮、图片处理流程1、图片处理流程2、图片处理流程3,和一个图片显示框。用户可以选择三个流程中的一种,进行图片处理。
3.首页选择照片后,显示框显示选择的图片。用户选择一种流程,进行处理后,直接pop回首页,并显示处理后的图片。
4.处理流程1:只需要图片裁剪功能。
5.处理流程2:先到剪裁页面进行图片剪裁,再到滤镜页面给图片滤镜,最后到水印界面给图片加水印。
6.处理流程3:先到剪裁页面进行图片剪裁,再到到水印界面给图片加水印,最后滤镜页面给图片滤镜。
1.使用四个ViewController实现四个界面。
2.使用四个ModuleManager模块,一个是整个Demo的总模块,其他三个作为子模块,分别处理三个流程。
3.四个界面只实现各自的图片处理功能,不负责流程跳转。流程跳转由三个子模块负责实现。*
本示例将包含四个ViewController:
1.首页: MDMMImageHomeViewController
2.裁剪: MDMMImageCropViewController
3.滤镜: MDMMImageGrayViewController
4.水印: MDMMImageLogoViewController
三个图片处理功能界面,头文件仅包含两个对外接口,用以传入需要处理的图片,和回调已经处理后的图片(详细代码可参考Demo):
@property (nonatomic, copy) void (^didFinish)(UIImage *image);
- (instancetype)initWithImage:(UIImage *)image;
PS: 三个界面不需要任何push界面的代码,只需要在处理完成后,通过block回调即可,界面的push工作由三个流程(即ModuleManager)来管理。
本示例包含三个图片处理模块
MDMMImageProcess1<MDModuleManager> //只需要图片裁剪功能。
MDMMImageProcess2<MDModuleManager> //1.先到剪裁页面进行图片剪裁,2.再到滤镜页面给图片滤镜,3.最后到水印界面给图片加水印。
MDMMImageProcess3<MDModuleManager> //1.先到剪裁页面进行图片剪裁,2.再到到水印界面给图片加水印,3.最后滤镜页面给图片滤镜。
头文件也包含两个对外接口,用以传入需要处理的图片,和回调已经处理后的图片(详细代码可参考Demo):
@property (nonatomic, copy) void (^didFinish)(UIImage *image);
- (instancetype)initWithImage:(UIImage *)image navigationController:(UINavigationController *)navigationController;
处理流程1
PS:记得使用 ImplementationProtocol 来接入MDModuleManager的默认实现
@implementation MDMMImageProcess1
__ImplementationProtocol__
- (instancetype)initWithImage:(UIImage *)image navigationController:(UINavigationController *)navigationController {
if (self = [self initWithNavigationController:navigationController]) {
self.originImage = image;
}
return self;
}
//剪裁图片界面
- (UIViewController *)generateRootViewController {
MDMMImageCropViewController *vc = [[MDMMImageCropViewController alloc] initWithImage:self.originImage];
__weak typeof (self) _w_self = self;
vc.didFinish = ^(UIImage * _Nonnull image) {
if (_w_self.didFinish) {
_w_self.didFinish(image);
}
};
return vc;
}
@end
处理流程2
PS:记得使用 ImplementationProtocol 来接入MDModuleManager的默认实现
@implementation MDMMImageProcess2
__ImplementationProtocol__
- (instancetype)initWithImage:(UIImage *)image navigationController:(UINavigationController *)navigationController {
if (self = [self initWithNavigationController:navigationController]) {
self.originImage = image;
}
return self;
}
//剪裁图片界面
- (UIViewController *)generateRootViewController {
MDMMImageCropViewController *vc = [[MDMMImageCropViewController alloc] initWithImage:self.originImage];
__weak typeof (self) _w_self = self;
vc.didFinish = ^(UIImage * _Nonnull image) {
[_w_self grayImage:image];
};
return vc;
}
//滤镜界面
- (void)grayImage:(UIImage *)image {
MDMMImageGrayViewController *vc = [[MDMMImageGrayViewController alloc] initWithImage:image];
__weak typeof (self) _w_self = self;
vc.didFinish = ^(UIImage * _Nonnull image) {
[_w_self logoImage:image];
};
[self pushViewController:vc animated:YES];
}
//添加水印界面
- (void)logoImage:(UIImage *)image {
MDMMImageLogoViewController *vc = [[MDMMImageLogoViewController alloc] initWithImage:image];
__weak typeof (self) _w_self = self;
vc.didFinish = ^(UIImage * _Nonnull image) {
[_w_self finish:image];
};
[self pushViewController:vc animated:YES];
}
- (void)finish:(UIImage *)image {
if (self.didFinish) {
self.didFinish(image);
}
}
@end
处理流程3
PS:记得使用 ImplementationProtocol 来接入MDModuleManager的默认实现
@implementation MDMMImageProcess3
__ImplementationProtocol__
- (instancetype)initWithImage:(UIImage *)image navigationController:(UINavigationController *)navigationController {
if (self = [self initWithNavigationController:navigationController]) {
self.originImage = image;
}
return self;
}
//剪裁图片界面
- (UIViewController *)generateRootViewController {
MDMMImageCropViewController *vc = [[MDMMImageCropViewController alloc] initWithImage:self.originImage];
__weak typeof (self) _w_self = self;
vc.didFinish = ^(UIImage * _Nonnull image) {
[_w_self logoImage:image];
};
return vc;
}
//添加水印界面
- (void)logoImage:(UIImage *)image {
MDMMImageLogoViewController *vc = [[MDMMImageLogoViewController alloc] initWithImage:image];
__weak typeof (self) _w_self = self;
vc.didFinish = ^(UIImage * _Nonnull image) {
[_w_self grayImage:image];
};
[self pushViewController:vc animated:YES];
}
//滤镜界面
- (void)grayImage:(UIImage *)image {
MDMMImageGrayViewController *vc = [[MDMMImageGrayViewController alloc] initWithImage:image];
__weak typeof (self) _w_self = self;
vc.didFinish = ^(UIImage * _Nonnull image) {
[_w_self finish:image];
};
[self pushViewController:vc animated:YES];
}
- (void)finish:(UIImage *)image {
if (self.didFinish) {
self.didFinish(image);
}
}
@end
如果要使用三个图片处理流程之一,只需要在首页,push对应的ModuleManager即可
@implementation MDMMImageHomeViewController
- (void)viewDidLoad {
//...
[self.button1 addTarget:self action:@selector(process1) forControlEvents:UIControlEventTouchUpInside];
[self.button2 addTarget:self action:@selector(process2) forControlEvents:UIControlEventTouchUpInside];
[self.button3 addTarget:self action:@selector(process3) forControlEvents:UIControlEventTouchUpInside];
//...
}
- (void)process1 {
//创建ModuleManager
MDMMImageProcess1 *mm = [[MDMMImageProcess1 alloc] initWithImage:self.imageView.image navigationController:self.navigationController];
//推入ModuleManager
[self.moduleManager pushSubModuleManager:mm animated:YES];
//回调逻辑
__weak typeof(self) _w_self = self;
__weak typeof(mm) _w_mm = mm;
mm.didFinish = ^(UIImage * _Nonnull image) {
//设置首页控件显示
_w_self.imageView.image = image;
_w_self.button1.enabled = NO;
_w_self.button2.enabled = NO;
_w_self.button3.enabled = NO;
//pop掉整个ModuleManager
[_w_mm popAllViewControllersAnimated:YES];
};
}
- (void)process2 {
MDMMImageProcess2 *mm = [[MDMMImageProcess2 alloc] initWithImage:self.imageView.image navigationController:self.navigationController];
[self.moduleManager pushSubModuleManager:mm animated:YES];
__weak typeof(self) _w_self = self;
__weak typeof(mm) _w_mm = mm;
mm.didFinish = ^(UIImage * _Nonnull image) {
_w_self.imageView.image = image;
_w_self.button1.enabled = NO;
_w_self.button2.enabled = NO;
_w_self.button3.enabled = NO;
[_w_mm popAllViewControllersAnimated:YES];
};
}
- (void)process3 {
MDMMImageProcess3 *mm = [[MDMMImageProcess3 alloc] initWithImage:self.imageView.image navigationController:self.navigationController];
[self.moduleManager pushSubModuleManager:mm animated:YES];
__weak typeof(self) _w_self = self;
__weak typeof(mm) _w_mm = mm;
mm.didFinish = ^(UIImage * _Nonnull image) {
_w_self.imageView.image = image;
_w_self.button1.enabled = NO;
_w_self.button2.enabled = NO;
_w_self.button3.enabled = NO;
[_w_mm popAllViewControllersAnimated:YES];
};
}
@end