ios app唤起页面跳转
有些时候我们需要再其他地方把app唤起,并打开跳转到指定的vc上面。这里我自己写了一个vc的mgr,最主要的技术是method swizzle。原理就不详述,看代码吧。
//// 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 BaseViewControllerUIViewController * g_lastViewController = nil;#pragma mark -#pragma mark implement ViewControllerMgrstatic 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