ios app唤起页面跳转

有些时候我们需要再其他地方把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





你可能感兴趣的:(ios app唤起页面跳转)