您的位置:首页 > 移动开发 > IOS开发

iOS关于如何让<界面切换逻辑>与<数据业务逻辑>解耦的探讨

2014-08-22 11:03 549 查看
在各种问答社区里面游荡的时候,经常会有人问到类似于:

怎么在tableviewcell里面点击一个button让uitableviewcontroller切换到另外一个controller呢?

其实这个问题遇到的情况很多,其实还是View与Controller的代码解藕的问题。

这篇博客只是以这个问题的解决为目的:

最早几年前接触iOS的时候用了一个当时很火的库:Three20 from facebook。但是随着编程思维的转变,发现这样的库有几个不好的地方:

1. 太过于重量级,什么功能都有

2. 自定义比较麻烦

3. 库内部的模块代码耦合太严重

所以我们后来花了很大的力气,把这个库完全去除。后来我们搭建iOS工程的基本是不会选择大型库的。典型的一个例子:AFNetworking VS
MKNetworkKit 这个2个库我们选择的是MKNetworkKit,很显然后者更轻量级。AFNetworking做了很多其他的工作,比如:图片异步加载,但是显然这些工作会有更优秀的第三方库类似于SDWebImage 来完成。

再回到一开始的问题,这也是一个解藕的问题,那么我是怎么做的呢?解决方法来源于Three20的TTNavigator,我把他从Three20框架中剥离了出来,并同时优化了一下(不再是 url-base-navigation)。

直接上代码了:

JKNavigator.h
#import <Foundation/Foundation.h>

/************************************************************************
* Dependencies:
JKSingletonObject,
JKViewController.
* Based on: TTNavigator.
************************************************************************/

#import "JKSingletonObject.h"

@interface JKNavigator : JKSingletonObject

JK_SINGLETON_INSTANCE_METHOD_INTERFACE(JKNavigator)

/**
* The window that contains the view controller hierarchy.
*/
@property (nonatomic, readonly) UIWindow *window;

/**
* The controller that is at the root of the view controller hierarchy.
*/
@property (nonatomic, readonly) UIViewController* rootViewController;
@property (nonatomic, readonly) UINavigationController* topNavigationController;

/**
* Removes all view controllers from the window and releases them.
*/
- (void)removeAllViewControllers;

/**
* Register class.
*/
- (void)registerNavigationSubclass:(Class)navigationSubclass;

/*!
* Set navigaiton controller view background color.
*/
@property (nonatomic, strong) UIColor *navigationControllerBackgroundColor;

/**
* Push view controller in global navigator.
* lightweight API for [Push & Present ViewController].
* I think it's more suitable than Url-based in Three20 for us. Here are some reasons:
* => 1. APIs look like official APIs.
* => 2. Easy to know the Class name without importing the header.
* => 3. More controllable for us.
*/
- (UIViewController *)pushViewController:(NSString *)classString
animated:(BOOL)animated;
- (UIViewController *)pushViewController:(NSString *)classString
withQuery:(id)query
animated:(BOOL)animated;

- (UIViewController *)pushNibViewController:(NSString *)classString
animated:(BOOL)animated;
- (UIViewController *)pushNibViewController:(NSString *)classString
withQuery:(id)query
animated:(BOOL)animated;

- (UIViewController *)presentViewController:(NSString *)classString
animated:(BOOL)animated;
- (UIViewController *)presentViewController:(NSString *)classString
withQuery:(id)query
animated:(BOOL)animated;
- (UIViewController *)presentViewController:(NSString *)classString
withQuery:(id)query
animated:(BOOL)animated
completion:(void(^)(void))completion;

- (UIViewController *)presentNibViewController:(NSString *)classString
animated:(BOOL)animated;
- (UIViewController *)presentNibViewController:(NSString *)classString
withQuery:(id)query
animated:(BOOL)animated;
- (UIViewController *)presentNibViewController:(NSString *)classString
withQuery:(id)query
animated:(BOOL)animated
completion:(void(^)(void))completion;

@end

JKNavigator.m
#import "JKNavigator.h"
#import "JKViewController.h"

@interface JKNavigator ()

@property (nonatomic, strong) Class navigationSubclass;

@end

@implementation JKNavigator

@synthesize navigationSubclass = _navigationSubclass;
JK_SINGLETON_INSTANCE_METHOD_IMPL(JKNavigator)

#pragma mark - Getter & Setter

- (Class)navigationSubclass
{
if (!_navigationSubclass) {
_navigationSubclass = [UINavigationController class];
}

return _navigationSubclass;
}

- (void)setNavigationSubclass:(Class)navigationSubclass
{
if ([navigationSubclass isSubclassOfClass:[UINavigationController class]]) {
_navigationSubclass = navigationSubclass;
} else {
@throw [NSException exceptionWithName:@"Incorrect Class" reason:@"You can only set UINavigationController subclass here." userInfo:@{}];
}
}

- (UIWindow *)window
{
return [UIApplication sharedApplication].keyWindow;
}

- (UIViewController *)rootViewController
{
return self.window.rootViewController;
}

- (UINavigationController *)topNavigationController
{
UINavigationController *root = (UINavigationController *)self.rootViewController;

while (root) {
UINavigationController *child = (UINavigationController *)root.presentedViewController;
if (child && [child isKindOfClass:[UINavigationController class]]) {
root = child;
} else {
return root;
}
}

return nil;
}

#pragma mark - Remove all.
- (void)removeAllViewControllers
{
UINavigationController *rootNavigator = (UINavigationController *)self.rootViewController;
UINavigationController *root = (UINavigationController *)self.rootViewController;
while (root) {
if (root && [root isKindOfClass:[UINavigationController class]]) {
[root dismissViewControllerAnimated:NO completion:NULL];
}

root = (UINavigationController *)root.presentedViewController;;
}

[rootNavigator popToRootViewControllerAnimated:NO];
}

#pragma mark - Private methods
- (UIViewController *)pushViewController:(NSString *)classString
withNib:(BOOL)witNib
withQuery:(id)query
animated:(BOOL)animated
{
return [self showViewControllerWithAction:@"Push"
classString:classString
withNib:witNib
withQuery:query
animated:animated
completion:NULL];
}

- (UIViewController *)presentViewController:(NSString *)classString
withNib:(BOOL)witNib
withQuery:(id)query
animated:(BOOL)animated
completion:(void(^)(void))completion
{
return [self showViewControllerWithAction:@"Present"
classString:classString
withNib:witNib
withQuery:query
animated:animated
completion:completion];
}

- (UIViewController *)showViewControllerWithAction:(NSString *)action
classString:(NSString *)classString
withNib:(BOOL)witNib
withQuery:(id)query
animated:(BOOL)animated
completion:(void(^)(void))completion
{
UIViewController *controller = nil;
UINavigationController *topNavigationController = [self topNavigationController];

// Generate UIViewController from classString.
Class class = NSClassFromString(classString);
if ([class isSubclassOfClass:[JKViewController class]]) {
controller = witNib ? [[class alloc] initWithNibAndQuery:query] : [[class alloc] initWithQuery:query];
} else if ([class isSubclassOfClass:[UIViewController class]]) {
controller = witNib ? [[class alloc] initWit
9e19
hNibName:classString bundle:nil] : [[class alloc] init];
}

// Show controller.
if (controller && topNavigationController) {
if ([action isEqualToString:@"Push"]) {
[topNavigationController pushViewController:controller animated:animated];
} else if ([action isEqualToString:@"Present"]) {
if ([controller isKindOfClass:[UINavigationController class]]) {
[topNavigationController presentViewController:controller animated:animated completion:completion];
} else {
UINavigationController *navigation = [[self.navigationSubclass alloc] initWithRootViewController:controller];
navigation.view.backgroundColor = self.navigationControllerBackgroundColor;
[topNavigationController presentViewController:navigation animated:animated completion:completion];
}
}
}

return controller;
}

#pragma mark - Push methods
- (void)registerNavigationSubclass:(Class)navigationSubclass;
{
self.navigationSubclass = navigationSubclass;
}

#pragma mark
- (UIViewController *)pushViewController:(NSString *)classString animated:(BOOL)animated
{
return [self pushViewController:classString withNib:NO withQuery:nil animated:animated];
}

- (UIViewController *)pushViewController:(NSString *)classString withQuery:(id)query animated:(BOOL)animated
{
return [self pushViewController:classString withNib:NO withQuery:query animated:animated];
}

- (UIViewController *)pushNibViewController:(NSString *)classString animated:(BOOL)animated
{
return [self pushViewController:classString withNib:YES withQuery:nil animated:animated];
}

- (UIViewController *)pushNibViewController:(NSString *)classString withQuery:(id)query animated:(BOOL)animated
{
return [self pushViewController:classString withNib:YES withQuery:query animated:animated];
}

#pragma mark

- (UIViewController *)presentViewController:(NSString *)classString animated:(BOOL)animated
{
return [self presentViewController:classString withNib:NO withQuery:nil animated:animated completion:NULL];
}

- (UIViewController *)presentViewController:(NSString *)classString withQuery:(id)query animated:(BOOL)animated
{
return [self presentViewController:classString withNib:NO withQuery:query animated:animated completion:NULL];
}

- (UIViewController *)presentViewController:(NSString *)classString withQuery:(id)query animated:(BOOL)animated completion:(void(^)(void))completion
{
return [self presentViewController:classString withNib:NO withQuery:query animated:animated completion:completion];
}

- (UIViewController *)presentNibViewController:(NSString *)classString animated:(BOOL)animated
{
return [self presentViewController:classString withNib:YES withQuery:nil animated:animated completion:NULL];
}

- (UIViewController *)presentNibViewController:(NSString *)classString withQuery:(id)query animated:(BOOL)animated
{
return [self presentViewController:classString withNib:YES withQuery:query animated:animated completion:NULL];
}

- (UIViewController *)presentNibViewController:(NSString *)classString withQuery:(id)query animated:(BOOL)animated completion:(void(^)(void))completion
{
return [self presentViewController:classString withNib:YES withQuery:query animated:animated completion:completion];
}

@end


当然其中还部分牵扯到了其他的class,可以根据你的需求去除。JKNavigator是一个singleton object。使用时异常简单:
[[JKNavigator instance] pushViewController:@"YourViewControllerClassString" animated:YES];
[[JKNavigator instance] pushNibViewController:@"YourViewControllerClassString" animated:YES];

不需要包含任何头文件,在任何地方都可以进行push操作。极大程序上解藕了View和Controller。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息