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,不管在哪个页面都要跳转对应的页面》大家要是有更好的实现思路欢迎交流~~