iOS-runtime实践-消息推送跳转到任意页面并赋值
2017-04-17 本文已影响106人
c6e16b2e3a16
runtime的概念和作用不在这里多阐述,网上的文章很多.runtime的功能虽然很强大,但是在实际项目中写业务代码时很少用到.字典转Model已经有很好的轮子了,我们不用再自己去写(学习除外);非特殊情况,替换系统方法也几乎不会用到.本文介绍一种常见的runtime应用场景:消息推送.
应用收到消息推送时,会根据推动内容跳转到相应页面(控制器).推送消息的内容是以key:value
的形式发送的,因此需要根据key:value
来动态生成页面并跳转.
问题转换:根据字典来动态创建控制器并跳转.
1.约定推送消息(字典)格式
下图是友盟推送后台,消息推送时设置key:value
键值对

因为要跳转到指定页面(控制器),则必须有一个键值对传递目标控制器的名称.其他的键值对可以是该页面所需要的属性值.
假设我的应用中有一个个人页面,需要根据推送内容显示推送的用户信息:
#import "PersonalViewController.h"
@interface PersonaViewController ()
@property (copy, nonatomic) NSString *name;
@property (assign, nonatomic) NSInteger age;
@property (copy, nonatomic) NSString *tel;
@end
@implementation PersonalViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
NSLog(@"%@, %ld, %@", _name, _age, _tel);
}
@end
那么推送消息内容可以这样设置:

className是类名称
其他参数是该类所需要的属性值
2.APP接收通知并跳转
创建UINavigationController消息通知处理分类方便管理.
#import <UIKit/UIKit.h>
@interface UINavigationController (RemoteNotify)
- (void)pushWithUserInfo:(NSDictionary *)userInfo;
@end
#import "UINavigationController+RemoteNotify.h"
#import <objc/runtime.h>
@implementation UINavigationController (RemoteNotify)
- (void)pushWithUserInfo:(NSDictionary *)userInfo {
if ([userInfo.allKeys containsObject:@"className"]) {
NSString *className = [userInfo objectForKey:@"className"];
Class class = NSClassFromString(className);
id instance = [[class alloc] init];
if (instance) {
[userInfo enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
if (![key isEqualToString:@"className"]) {
if ([self containsProperty:instance key:key]) {
[instance setValue:obj forKey:key];
} else {
NSLog(@"不包含(%@:%@)", key, obj);
}
}
}];
[self pushViewController:instance animated:YES];
}
}
}
- (BOOL)containsProperty:(id)instance key:(NSString *)key {
unsigned int count;
objc_property_t *properties = class_copyPropertyList([instance class], &count);
for (int i = 0; i < count; i++) {
objc_property_t property = properties[i];
NSString *name = [NSString stringWithCString: property_getName(property) encoding:NSUTF8StringEncoding];
if ([key isEqualToString:name]) {
return YES;
}
}
free(properties);
return NO;
}
@end
在AppDelegate的消息接收方法中调用
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
//获取当前控制器
UITabBarController *rootCtrl = (UITabBarController *)self.window.rootViewController;
UINavigationController *currentCtrl = (UINavigationController *)rootCtrl.selectedViewController;
//从当前控制器push到指定控制器
[currentCtrl pushWithUserInfo:userInfo];
}