消息推送---跳转到指定页面

2020年1月6日更新

现在看看当初自己的解决方案简直不堪入目。找个时间记录一下最新最简单的解决方案。

方案一:

利用OC的反射机制:和服务端协商字段定义,想要跳转的VC类名,VC需要数据的属性名。
例如: 打开push跳转商品详情页。
服务端数据:

"vc": "GoodsDetailViewController",
"data": {
"goodsID": "123456"
}

其中GoodsDetailViewController对应我们项目中的商品详情页VC,goodsID是打开页面需要的参数。
用反射机制得到VC,KVC进行成员变量赋值。

Class class = NSClassFromString(@"GoodsDetailViewController");
UIViewController *vc = [[class alloc] init];
[vc setValue:@"123456" forKey:@"goodsID"];
[self.navigationController pushViewController:vc animated:YES];
  • 缺点:
    安卓,iOS必须保证类名,变量名,方法名,统一并且不能随意修改参数,不够灵活。(或者服务端进行兼容,不同的操作系统吐不同的数据结构。)
    不能进行数据的二次包装,修改。
    不支持附加动作,例如打开一个特定页面顺便发个全局通知。
  • 优点:
    代码简单,不用维护庞大的路由表。

总结:利用反射机制做路由,只支持简单,单一的业务场景。例如 push推送打开页面。

方案二:

建立路由系统:
Scheme:wonderfull
Host: mall
例如:
商品详情页的跳转action:wonderfull://mall/goods_detail?goods_id=?&house_id(可选)=?
页面的跳转和功能的触发,写在NSString的分类里。

  • 缺点:
    需要维护庞大的路由表,并且随着页面和功能的增加,或越发臃肿。
  • 优点:
    灵活,可以二次包装数据。
    数据结构简单,一个string就搞定。

总结:灵活多变,适合复杂的业务场景,就是需要维修臃肿的路由表。豌豆公主App选择的是这种路由方案。

以下是之前的方案,可忽略

最近在弄友盟推送,点击推送的消息,打开App,不管在哪个页面都要跳转对应的页面。其中在跳转对应的页面这块卡了,在这里记录一下自己的解决方法,不是最好的。希望有更好的方法或者思路可以留言。

友盟推送设置

AppDelegate.m文件里:
/*
 ** 友盟推送
 */
- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(nullable NSDictionary *)launchOptions{
    
    [UMessage startWithAppkey:@"你的Appkey" launchOptions:launchOptions];
    [UMessage registerForRemoteNotifications];
    
    UIMutableUserNotificationAction *action1 = [[UIMutableUserNotificationAction alloc] init];
    action1.identifier = @"action1_identifier";
    action1.title=@"Accept";
    action1.activationMode = UIUserNotificationActivationModeForeground;//当点击的时候启动程序
    
    UIMutableUserNotificationAction *action2 = [[UIMutableUserNotificationAction alloc] init];  //第二按钮
    action2.identifier = @"action2_identifier";
    action2.title=@"Reject";
    action2.activationMode = UIUserNotificationActivationModeBackground;//当点击的时候不启动程序,在后台处理
    action2.authenticationRequired = YES;//需要解锁才能处理,如果action.activationMode = UIUserNotificationActivationModeForeground;则这个属性被忽略;
    action2.destructive = YES;
    
    UIMutableUserNotificationCategory *actionCategory = [[UIMutableUserNotificationCategory alloc] init];
    actionCategory.identifier = @"category1";//这组动作的唯一标示
    [actionCategory setActions:@[action1,action2] forContext:(UIUserNotificationActionContextDefault)];
    
    NSSet *categories = [NSSet setWithObject:actionCategory];
    
    //如果默认使用角标,文字和声音全部打开,请用下面的方法
    [UMessage registerForRemoteNotifications:categories];
    NSLog(@"------友盟推送设置------");
    
   // [UMessage setLogEnabled:YES];
    return YES;
}

获取Device Token用来测试模式

AppDelegate.m文件里:

/**
 *
 *  获取Device Token
 *  @param application
 *  @param deviceToken
 */
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken{

    NSLog(@"------Device Token:%@",[[[[deviceToken description] stringByReplacingOccurrencesOfString: @"<" withString: @""]                  stringByReplacingOccurrencesOfString: @">" withString: @""]                 stringByReplacingOccurrencesOfString: @" " withString: @""]);
    
}

收到推送消息的三种情况:

AppDelegate.m文件里:

/**
 * 接收推送来的数据并作对应处理
 */
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
    self.userInfo = userInfo;
    //关闭友盟自带的弹出框
    [UMessage setAutoAlert:NO];
    [UMessage didReceiveRemoteNotification:userInfo];
    NSString* alert=[userInfo[@"aps"] valueForKey:@"alert"];
    
    NSLog(@"-------推送的数据-------%@",userInfo);
    
    //定制自定的的弹出框
    if([UIApplication sharedApplication].applicationState == UIApplicationStateActive)//App是打开状态
    {
        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"大料爆料"
                                                            message:alert
                                                           delegate:self
                                                  cancelButtonTitle:@"朕去看看"
                                                  otherButtonTitles:@"朕知道啦",nil];
        [alertView show];
    }else{
        
        [[NSNotificationCenter defaultCenter] postNotificationName:@"userInfoNotification" object:self userInfo:self.userInfo];
    }
}

第一种

App是关闭的,点击推送消息直接唤醒App。然后跳转指定页面,这种情况简单,在首页直接加个通知,打开就行。这种情况进来App肯定在首页,好处理。

第二种

App是在后台运行的,你唤醒App之后,不确定是哪个页面。初步想法是在每个页面都加上通知,用来接收消息。这里有个方法可以统一加通知:

我的项目是自定义NavigationController,在LeoNavigationController.m中加上代码:

//每个push进来的ViewController都会走这个方法,这样就能统一给ViewController加上通知

-(void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated{

//给每个push进来的控制器加通知

[viewController gettingNotification];

}

gettingNotification这个方法我写在了viewController得扩展里:

#import "UIViewController+catagory.h"
#import "MoreInforViewController.h"

static NSInteger leo = 0;
@implementation UIViewController (catagory)

-(void)gettingNotification{
    
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(userInfoNotification:) name:@"userInfoNotification" object:nil];
    leo = 0;
    NSLog(@"--------设置接受通知--------");
}

-(void)userInfoNotification:(NSNotification*)notification{
    
    if (leo == 0) {
        
        NSDictionary *dict = [notification userInfo];
        NSLog(@"-----推送通知来的数据-----:%@",dict);
        NSString* url=[dict valueForKey:@"url"];
        
        NSString* type=[[NSString alloc]init];
        type = [dict valueForKey:@"type"];
        if (type == nil) {
            type = @"stroy";
        }
        NSLog(@"-----推送通知来的数据-----:%@",type);
        if ([type isEqualToString:@"stroy"]) {
            
            NSLog(@"-----加载推送来的链接-----:%@",url);
            MoreInforViewController* vc = [[MoreInforViewController alloc]init];
            vc.url = url;
            [[self getCurrentVC].navigationController pushViewController:vc animated:YES];
            leo++;
        }  
    }
}

/**
 *  获取当前的屏幕展示的控制器
 *
 *  @return 控制器
 */

- (UIViewController *)getCurrentVC{
    
    UIViewController *result = nil;
    UIWindow * window = [[UIApplication sharedApplication] keyWindow];
    //app默认windowLevel是UIWindowLevelNormal,如果不是,找到UIWindowLevelNormal的
    if (window.windowLevel != UIWindowLevelNormal)
    {
        NSArray *windows = [[UIApplication sharedApplication] windows];
        for(UIWindow * tmpWin in windows)
        {
            if (tmpWin.windowLevel == UIWindowLevelNormal)
            {
                window = tmpWin;
                break;
            }
        }
    }
    id  nextResponder = nil;
    UIViewController *appRootVC=window.rootViewController;
    //    如果是present上来的appRootVC.presentedViewController 不为nil
    if (appRootVC.presentedViewController) {
        nextResponder = appRootVC.presentedViewController;
    }else{
        UIView *frontView = [[window subviews] objectAtIndex:0];
        nextResponder = [frontView nextResponder];
        //<span style="font-family: Arial, Helvetica, sans-serif;">//  这方法下面有详解    </span>
    }
    
    if ([nextResponder isKindOfClass:[UITabBarController class]]){
        UITabBarController * tabbar = (UITabBarController *)nextResponder;
        UINavigationController * nav = (UINavigationController *)tabbar.viewControllers[tabbar.selectedIndex];
        //UINavigationController * nav = tabbar.selectedViewController ; 上下两种写法都行
        result=nav.childViewControllers.lastObject;
        
    }else if ([nextResponder isKindOfClass:[UINavigationController class]]){
        UIViewController * nav = (UIViewController *)nextResponder;
        result = nav.childViewControllers.lastObject;
    }else{
        result = nextResponder;
    }
    NSLog(@"------当前展示的控制器------%@",result);
    return result;
}

这里有两个坑~
刚开始,我只是把所有的界面都注册通知了,然后推送消息,发现你打开过的控制器,都会push你要推送的那个页面。后来我想方法获取了当前屏幕展示的控制器。这样才能在我想要的控制器里push。
因为注册通知我写在UIViewController的扩展里,所以如果我通知了5个控制器,那个通知方法会走5遍push5个页面。加个leo字段控制只让其走一次。

第三种

推送的时候,App就是打开的状态。这里处理就是加一个弹窗,让用户选择一下就行。我在之前AppDelegate.m代码中已经写了。剩下的和第二种情况一样。

后记

其实如果自定义过UIViewController,你所有的ViewController都是继承它。就不用写扩展了。直接在父类中注册通知就行。

至于为什么不在LeoNavigationController.m中的pushViewController这个方法中直接对push进来的每个ViewController进行通知注册。我试了,不过不知道怎么回事,推送消息走到这里就崩溃了。当时赶项目,就没仔细研究,换一个方法。

关于《打开App,不管在哪个页面都要跳转对应的页面》大家要是有更好的实现思路欢迎交流~~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容