Skip to content

MDModuleManager

剑川道长 edited this page Apr 23, 2019 · 6 revisions

MDModuleManager依赖:

1.协议默认实现MDProtocolImplementation 查看介绍

2.面向切面Aspects 查看介绍

综述

ModuleManager是一款处理界面模块化的架构。他具备高度封装、业务逻辑高度分离等特性,还能使得界面间跳转逻辑单独分离,从而达到界面功能高度复用,无需为流程的改动、增减而重写界面代码的效果。

特点

1.兼容性

MDModuleManager 与传统的MVC框架完全兼容,可以灵活选择是否使用MDModuleManager

2.协议化

MDModuleManager 不是一个基类,而是一个协议。并且使用MDProtocolImplementation 提供了协议的默认实现。因此用户可以用任何类,作为MDModuleManager。

3.模块化

ModuleManager 可以被推入(Push),也可以作为一个整体被弹出(Pop)。推入ModuleManager的角色,无需关心Module中的界面,直接push ModuleManager,或者push ModuleManager.rootViewController即可。ModuleManager作为一个整体,也可以调用其popAll方法,来弹出该模块所有界面。当然popAll是有限制的,只有当模块所管理的界面栈的最后一个界面,也是当前NavigationController所管理的界面栈的最后一个界面时,才可以popAll。

4.可嵌套

一个父ModuleManager可以推入一个子ModuleManager。如果父ModuleManager符合popAll条件,可以直接调用父ModuleManager的popAll,来pop掉所有界面,此时子ModuleManager界面也会被弹出。

5.业务逻辑分离

在传统的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.使用范围

1.可以直接在AppDelegate中生成一个ModuleManager,然后用它来管理整个App的界面流。

2.可以直接利用navigationController,来push ModuleManager.rootViewController。来推入一个模块。

3.可以使用[MDModuleManager pushSubModuleManager: animated:]方法,来推入一个子模块

PS:由于ModuleManager的兼容性,以上三中使用方式,可以任意选择使用或者不使用

2.创建模块范例

a) .h文件

//MDMMImageModuleManager.h
#import <Foundation/Foundation.h>
#import "MDModuleManager.h"


NS_ASSUME_NONNULL_BEGIN

@interface MDMMImageModuleManager : NSObject <MDModuleManager>

@end

NS_ASSUME_NONNULL_END

b) .m文件

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

3.使用ModuleManager范例

a) 在window上使用

- (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;
}

b) 使用NavigationController推入

- (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];
}

c) 使用ModuleManager推入子ModuleManager

- (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];
    //...
}

d) 使用ModuleManager推入界面

//MDMMImageModuleManager.m

@implementation MDMMImageModuleManager
__ImplementationProtocol__

//...
- (void)pushViewController {
    UIViewController *viewController = [[UIViewController alloc] init];
    [self pushViewController:viewController  animated:YES]
}
//...

@end

图像处理Demo

后面我们将以图像处理Demo,作为范例,来进一步演示这个框架。

图像处理Demo的所有代码,在Demo工程中的 **MDToolsDemo/MDModuleManagerDemo_image** 文件夹下,运行时需要将main.m文件中的AppDelegate修改为 **DemoAppDelegate_image** 类。

需求假定

1.Demo功能简述:

用户可以在首页选择一张图片显示在首页上,然后可以选择三种图片编辑流程来编辑图片。无论选择哪种处理流程,在处理完毕后,所有处理流程中的功能界面,都会被pop掉,回到首页。然后需要把处理完的图片显示在首页上。

2.Demo功能详细描述:

1.Demo共四个界面:首页、图片裁剪页面、图片滤镜、图片加水印

2.首页:提供四个按钮:选择照片按钮、图片处理流程1、图片处理流程2、图片处理流程3,和一个图片显示框。用户可以选择三个流程中的一种,进行图片处理。

3.首页选择照片后,显示框显示选择的图片。用户选择一种流程,进行处理后,直接pop回首页,并显示处理后的图片。

4.处理流程1:只需要图片裁剪功能。

5.处理流程2:先到剪裁页面进行图片剪裁,再到滤镜页面给图片滤镜,最后到水印界面给图片加水印。

6.处理流程3:先到剪裁页面进行图片剪裁,再到到水印界面给图片加水印,最后滤镜页面给图片滤镜。

模块划分

1.使用四个ViewController实现四个界面。

2.使用四个ModuleManager模块,一个是整个Demo的总模块,其他三个作为子模块,分别处理三个流程。

3.四个界面只实现各自的图片处理功能,不负责流程跳转。流程跳转由三个子模块负责实现。*

Demo代码简介

1.ViewControllers

本示例将包含四个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)来管理。

2.ModuleManagers

本示例包含三个图片处理模块

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

3.使用ModuleManagers

如果要使用三个图片处理流程之一,只需要在首页,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

其他

API说明文档