有些时候我们需要再其他地方把app唤起,并打开跳转到指定的vc上面。这里我自己写了一个vc的mgr,最主要的技术是method swizzle。原理就不详述,看代码吧。
// // ViewControllerMgr.h // // // Created by Tommy on 13-8-14. // Copyright (c) 2013年 Tommy. All rights reserved. // #import <Foundation/Foundation.h> @protocol ViewControllerMgrDelegate <NSObject> - (BOOL) willCreateVC:(NSURL*)url; - (BOOL) willPresentVC:(UIViewController*)onVc currentVC:(UIViewController*) presentVC url:(NSURL*)url; //if return no, will not dispatch delay url - (BOOL) willDispatchDelayedUrl:(NSURL*)url; //- (BOOL) needchangeToNextVC:(UIViewController*)onVc; @optional //if return no, will not set the param by vcmgr //please set param by yourself in delegate, and return no - (BOOL) willSetParamter:(UIViewController*)onVc key:(NSString*)key value:(NSString*)value; @optional - (UIViewController*) creatViewController:(NSString*)vcKey paramters:(NSDictionary*)parameters; @end #define dispatch_delayed_notification_name @"_dispatchDelayedViewControllers" @interface ViewControllerMgr : NSObject @property(weak) id<ViewControllerMgrDelegate> delegate; +(id) sharedInstance; //如果当前的vc刚好和需要显示的vc是同一个类,如果不需要再这个之上弹出,而只是修改当前vc的内容,请设置为YES,否则为NO //默认为NO @property (assign) BOOL enablePresentOnSameVC; @property (strong) NSString * scheme; //保持需要被推迟的vc 的url @property (strong) NSMutableArray * delayedUrlArray; - (BOOL) handleUrl:(NSURL*)url; - (void) registerViewController:(NSString*)key ClassName:(NSString*)vcName; - (void) registerViewController:(NSString*)key Class:(Class) vcClass; - (void) registerViewController:(NSDictionary*)dic; //register vc init paramters - (void) registerInitParameters:(NSArray*) array ClassName:(NSString*)vcName; - (void) registerVCWithClassName:(NSString*)vcName; - (void) registerVCInitWithCoder:(NSCoder *)aDecoder ClassName:(NSString*)vcName; - (void) registerVCInitWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil ClassName:(NSString*)vcName; //delay - (void) addToDelay:(NSURL*)url; //call by - (void) dispatchDelayedViewControllers; - (void) addViewControllerToDispatchQueue:(UIViewController*)vc; //暂时不支持 //- (void) presentViewController:(NSString*)key; //- (void) presentModalViewController:(NSString*)key paramters:(NSString*)paramters; - (void) presentModalViewController:(NSURL*)url; - (UIView*) topView; - (UIViewController*) topViewController; @end
// // ViewControllerMgr.m // // // Created by Tommy on 13-8-14. // Copyright (c) 2013年 Tommy. All rights reserved. // #import "ViewControllerMgr.h" #import <objc/runtime.h> #import <objc/objc.h> //static TomStack* s_vcStack = nil; static NSMutableDictionary* s_vcInitParametersDic = nil; #pragma mark - #pragma mark implement BaseViewController UIViewController * g_lastViewController = nil; #pragma mark - #pragma mark implement ViewControllerMgr static ViewControllerMgr* s_vcmgr = nil; @implementation ViewControllerMgr { NSMutableDictionary* vcDic; BOOL dispatchOpened; } - (id) init { if(self =[super init]) { vcDic = [NSMutableDictionary new]; _enablePresentOnSameVC = NO; dispatchOpened = NO; [self installHook]; } return self; } +(id) sharedInstance { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ if (s_vcmgr == nil) { s_vcmgr = [[self alloc] init]; //autorelease]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dispatchDelayedViewControllers) name:dispatch_delayed_notification_name object:nil]; } }); return s_vcmgr; } +(id) allocWithZone:(NSZone *)zone { @synchronized(self) { if (s_vcmgr == nil) { s_vcmgr = [super allocWithZone:zone]; return s_vcmgr; } } return nil; } - (BOOL) handleUrl:(NSURL*)url { NSAssert(_scheme,@"scheme is null"); NSAssert(_delegate,@"delegate is null"); @try { if(url && _scheme && [_scheme isEqualToString:[url scheme]]) { [[ViewControllerMgr sharedInstance] presentModalViewController:url]; return YES; } } @catch (NSException *exception) { NSLog(@"严重错误!!!!!"); } return NO; } //register vc -(void) registerViewController:(NSString*)key ClassName:(NSString*)vcName { [self registerViewController:key Class:NSClassFromString(vcName)]; } -(void) registerViewController:(NSString*)key Class:(Class) vcClass { //if([vcClass isKindOfClass:[UIViewController class]]) [vcDic setObject:vcClass forKey:key]; } - (void) registerViewController:(NSDictionary*)dic { for(id obj in dic) { [self registerViewController:obj ClassName:[dic valueForKey:obj]]; } } //register #pragma mark - #pragma mark register vc init paramters - (void) registerInitParameters:(NSArray*) array ClassName:(NSString*)vcName { if(!s_vcInitParametersDic) { s_vcInitParametersDic = [NSMutableDictionary new]; } [s_vcInitParametersDic setValue:array forKey:vcName]; } - (void) registerVCWithClassName:(NSString*)vcName { [self registerInitParameters:@[[NSNull null]] ClassName:vcName]; } - (void) registerVCInitWithCoder:(NSCoder *)aDecoder ClassName:(NSString*)vcName { [self registerInitParameters:@[aDecoder?aDecoder:[NSNull null],[NSNull null]] ClassName:vcName]; } - (void) registerVCInitWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil ClassName:(NSString*)vcName { [self registerInitParameters:@[nibNameOrNil?nibNameOrNil:[NSNull null],nibBundleOrNil?nibBundleOrNil:[NSNull null],[NSNull null]] ClassName:vcName]; } //presetn vc - (NSDictionary*) parseURIQueryString:(NSString*)query { NSMutableDictionary* param = [[NSMutableDictionary alloc] initWithCapacity:2]; NSArray* array = [query componentsSeparatedByString:@"&"]; for(NSString* ss in array) { NSArray* key = [ss componentsSeparatedByString:@"="]; switch ([key count]) { case 1: [param setValue:@"" forKey:[key objectAtIndex:0]]; break; case 2: [param setValue:[key objectAtIndex:1] forKey:[key objectAtIndex:0]]; break; default: break; } } return param; } - (UIViewController*) createViewController:(NSString*) key parameters:(NSDictionary*) paramters { UIViewController* vc = nil; Class vcClass = [vcDic objectForKey:key]; if(vcClass) { if(_enablePresentOnSameVC && g_lastViewController && [g_lastViewController isKindOfClass:vcClass]) { [self setParametersForVC:g_lastViewController paramters:paramters]; } else { vc = [[vcClass alloc] initByVCMgr]; [self setParametersForVC:vc paramters:paramters]; } } else { NSAssert(0, @"call error %@ or %@ not inhert from BaseViewController",key,key); } return vc; } - (void) setParametersForVC:(UIViewController*)vc paramters:(NSDictionary*) paramters { for (id key in paramters) { @try { if(_delegate && [_delegate respondsToSelector:@selector(willSetParamter:key:value:)]) { if([_delegate willSetParamter:vc key:key value:[paramters valueForKey:key]]) { [vc setValue:[paramters valueForKey:key] forKey:key]; } } } @catch (NSException *exception) { NSLog(@"param invalid %@",paramters); // NSAssert(0, @"param invalid %@",paramters); } } } //- (void) presentViewController:(NSString*)key //{ // [self presentModalViewController:key paramters:nil]; //} - (void) presentModalViewController:(NSURL*)url { if([_delegate willCreateVC:url ]) { NSString* path = [[url pathComponents] lastObject]; NSString* key = path?path:[url host]; NSDictionary* parameters = [self parseURIQueryString:[url query]]; UIViewController* vc = nil; if([_delegate respondsToSelector:@selector(creatViewController:paramters:)]) { vc = [_delegate creatViewController:key paramters:parameters]; } if(!vc) vc = [self createViewController:key parameters:parameters]; if(vc && g_lastViewController) { UIViewController* onVC = g_lastViewController; if(onVC && [_delegate willPresentVC:onVC currentVC:vc url:url] && vc != onVC) { if(onVC.navigationController) { [onVC.navigationController pushViewController:vc animated:YES]; } else { //[vc setValue:@(YES) forKey:@"modalPresent"]; UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc]; [onVC presentModalViewController:nav animated:YES]; } } } } } - (UIView*) topView { return [[[[UIApplication sharedApplication] keyWindow] subviews] lastObject]; } - (UIViewController*) topViewController { return g_lastViewController; } - (void) addToDelay:(NSURL*)aurl { if(!_delayedUrlArray) { _delayedUrlArray = [NSMutableArray new]; } for (NSURL* url in _delayedUrlArray) { if([[url absoluteString] isEqualToString:[aurl absoluteString]]) { return; } } dispatchOpened = NO; [_delayedUrlArray addObject:aurl]; } - (void) dispatchDelayedViewControllers { dispatchOpened = YES; [self dispatchDelayedViewController]; } - (void) dispatchDelayedViewController { if ([_delayedUrlArray count]) { NSURL * url = [_delayedUrlArray objectAtIndex:0]; if([_delegate willDispatchDelayedUrl:url]) { if ([_delayedUrlArray count] ) { [_delayedUrlArray removeObject:url]; [self handleUrl:url]; } } } } - (void) addViewControllerToDispatchQueue:(UIViewController*)vc { } #pragma mark - #pragma mark hooked method imp //define #define Hooked_Orignal_Selector(_orgSelName) @selector(_vc_orignal_##_orgSelName) #define Hooked_Method(_name) _hooked_##_name #define Add_Method_To_Class(_class,_selName) do{ \ Method add_method = class_getInstanceMethod([self class], @selector(_selName)); \ IMP add_imp = method_getImplementation(add_method); \ class_addMethod(_class, @selector(_selName), add_imp, method_getTypeEncoding(add_method)); \ }while(0) #define HOOK_OBJC_CLASS(_class,_orgSelName,_hookedSelName) do{ \ Method org_method = class_getInstanceMethod(_class, @selector(_orgSelName)); \ Method rep_method = class_getInstanceMethod([self class], @selector(_hookedSelName)); \ IMP org_imp = method_getImplementation(org_method); \ class_addMethod(_class, Hooked_Orignal_Selector(_orgSelName), org_imp, method_getTypeEncoding(org_method)); \ IMP rep_imp = method_getImplementation(rep_method); \ class_replaceMethod(_class, @selector(_orgSelName), rep_imp, method_getTypeEncoding(org_method)); \ }while(0) #define Set_Instance_Var(_obj,_name,_value) objc_setAssociatedObject(_obj,"_append_"#_name,_value,OBJC_ASSOCIATION_RETAIN_NONATOMIC) #define Get_Instance_Var(_obj,_name) objc_getAssociatedObject(_obj,"_append_"#_name) #define REAL_SELF() UIViewController* realSelf = (UIViewController*)self - (void) installHook { @try { HOOK_OBJC_CLASS([UIViewController class],viewWillAppear:,Hooked_Method(viewDidAppearHooked:)); HOOK_OBJC_CLASS([UIViewController class],viewDidAppear:,Hooked_Method(viewDidAppear:)); HOOK_OBJC_CLASS([UIViewController class],presentModalViewController:animated:,Hooked_Method(presentModalViewController:animated:)); Add_Method_To_Class([UIViewController class],initByVCMgr); // Add_Method_To_Class([UIViewController class],_modalClose:); // Add_Method_To_Class([UIViewController class],_addCloseBtn:); // Add_Method_To_Class([UIViewController class],goBack); // Add_Method_To_Class([UIViewController class],goHome); //class_addProperty need decalre in interface //class_addIvar cannot support for exist class } @catch (NSException *exception) { NSLog(@"install hook occur exception"); } @finally { } } //hooked method //note //self not viewcontrollermgr, is viewcontroller instance // -(id) initByVCMgr { REAL_SELF(); NSArray * parameters = [s_vcInitParametersDic valueForKey:[NSString stringWithUTF8String:class_getName([self class])]]; NSAssert(parameters, @"%@ initByVCMgr failed :init parameter error",self); id bself = nil; switch ([parameters count]) { case 1: bself = [realSelf init]; break; case 2: bself = [realSelf initWithCoder:[parameters objectAtIndex:0]==[NSNull null]?nil:[parameters objectAtIndex:0]]; break; case 3: bself = [realSelf initWithNibName:[parameters objectAtIndex:0]==[NSNull null]?nil:[parameters objectAtIndex:0] bundle:[parameters objectAtIndex:1]==[NSNull null]?nil:[parameters objectAtIndex:1]]; break; default: NSAssert(parameters, @"%@ initByVCMgr failed:too many paramter:%@",self,parameters); break; } if(bself) { Set_Instance_Var(self,presentByMgr, @(YES)); } return bself; } - (void) Hooked_Method(viewWillAppear:(BOOL)animated) { [self performSelector:Hooked_Orignal_Selector(viewWillAppear:) withObject:@(animated)]; if(!g_lastViewController) { g_lastViewController = (UIViewController*)self; [[ViewControllerMgr sharedInstance] performSelector:@selector(dispatchDelayedViewController)]; } } - (void) Hooked_Method(viewDidAppearHooked:(BOOL)animated) { [self performSelector:Hooked_Orignal_Selector(viewDidAppear:) withObject:@(animated)]; UIViewController* realSelf = (UIViewController*) self; CGRect frame = realSelf.view.frame; if(frame.origin.x == frame.origin.y && frame.origin.x == 0) g_lastViewController = realSelf; [[ViewControllerMgr sharedInstance] performSelector:@selector(dispatchDelayedViewController)]; } - (void) Hooked_Method(presentModalViewController:(UIViewController *)modalViewController animated:(BOOL)animated) { if([modalViewController isKindOfClass:[UINavigationController class]]) { UINavigationController * nav = (UINavigationController*)modalViewController; if([nav.viewControllers count]) { Set_Instance_Var([nav topViewController],modalPresent, @(YES)); } }else { Set_Instance_Var(modalViewController,modalPresent, @(YES)); } [self performSelector:Hooked_Orignal_Selector(presentModalViewController:animated:) withObject:modalViewController withObject:@(animated)]; } @end