其实这篇文章,是上篇文章Runtime的应用部分的展开,从任意页面跳转任意页面这块主要不是Runtime的知识点,所以没在上篇文章中进行展开。
首先新建一个工程,并在storyboard上面拖出来一个UITabBarController,加上两个UINavgationController,再加上四个UIViewController,具体布局如图:
然后再建四个UIViewController子类,并绑定到Storyboard上面的ViewController上面,并设置Storyboard ID
然后storyboard的每个ViewController上面添加两个Label,并绑定到代码文件里面。然后给暖色One和暗色One的View添加点击一个手势,把点击事件绑定到代码文件里面,我的属性事件命名就随意了。
然后在手势点击事件方法,写上跳转到Two页面的代码。
下面我们新建五个DataModel,一个PushModel,四个相应的VC的DataModel,我就取名WarmOneDataModel ,WarmTwoDataModel , DeadOneDataModel,DeadTwoDataModel(命名随意)
然后在PushModel里面写上四个属性,类型则是另外四个Model
四个VC的Model里面的代码是一样的(主要是省事,实际项目里面肯定不是这样的了)
然后在几个对应的ViewController写上对应的DataModel类型的属性,再把topLabelStr和BottomLabelStr取出来赋值给两个Lable。
目前前期准备工作已经完成了,下面需要在AppDelegate里面开始处理推送过来的信息,已经跳转,传值逻辑了。
我新建了一个SwiftRunTimeTool 类
class func checkClassPerporty(object : AnyClass , propertyStr: String) -> Bool{
var count : UInt32 = 0
let propertys = class_copyPropertyList(object, &count)
for index in 0 ..< Int(count){
let name = property_getName(propertys?[index])
if let propertyName = String.init(validatingUTF8: name!){
if propertyName == propertyStr{
return true
}
}
}
return false
}
利用Runtime做KVC之前的保护
关于push过来的信息格式,需要和后台进行对接好,不然错误的key,name,会导致取不到想要的值
// let params = ["class" : "填入需要跳转VC名字字符串" , "property" : [ "跳入VC需要用到的数据Model":["数据Model属性字符串" : "数据Model属性值"] ] , "modelName" : "数据Model在pushModel中的名字"] as [String : Any]
//模拟推送过来的信息
func setPushParams() ->[String : AnyObject]{
let params = ["class" : "WarmColorOneViewController" , "property" : [ "WarmOneDataModel":["topLabelStr" : "我是推送过来的信息" , "bottomLabelStr" : "我也是推送过来的信息"] ] , "modelName" : "warmOneModel"] as [String : Any]
return params as [String : AnyObject]
}
然后再写一个方法
//处理推送信息进行跳转传值方法
func conductPushParams(params : [String : AnyObject]){
}
接下来在本地新建一个plist文件,新建四个item,key是四个类的类名字符串,value是对应的Storyboard ID
//处理推送信息进行跳转传值方法
func conductPushParams(params : [String : AnyObject]){
//先检查推送过来的字典里面Class存不存在
guard let className = params["class"] as? String else{
print("参数类名不存在")
return
}
//读取Plist文件
let filePath = Bundle.main.path(forResource: "VCStoryboardIDList", ofType: "plist") ?? ""
let vcDic : NSDictionary = NSDictionary(contentsOfFile: filePath) ?? NSDictionary()
//判断plist文件里面是否含有目标VC的Storyboard ID
if let vcID = vcDic[className] {
guard let nameSpace = Bundle.main.infoDictionary!["CFBundleExecutable"] else {
print("命名空间不存在")
return
}
//如果VC是与storyboard关联的通过下面的语句创建VC对象
let clsVC = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(withIdentifier: vcID as! String)
setPushModelInfo(clsVC: clsVC, params: params, nameSpace: nameSpace as! String)
}else{
guard let nameSpace = Bundle.main.infoDictionary!["CFBundleExecutable"] else {
print("命名空间不存在")
return
}
//如果不是storyboard绑定的UIViewController 则通过这种方式创建其对象
let vcClassTemp : AnyClass? = NSClassFromString((nameSpace as! String) + "." + className)
guard let vcClsType = vcClassTemp as? UIViewController.Type else{
print("无法转换为UIViewController类型")
return
}
//创建对应View Controller 对象
let clsVC = vcClsType.init()
setPushModelInfo(clsVC: clsVC, params: params, nameSpace: nameSpace as! String)
}
}
//因为conductPushParams里面if else里面重复代码挺多,所以我抽离出来重新写了一个方法
func setPushModelInfo(clsVC : UIViewController , params : [String : Any] , nameSpace : String){
//创建PushModel 对象
let pushModel = PushModel()
//根据 params["property"]里面的dataModel名字字符串,创建其对象,首先获取命名空间字符串
guard let propertyDic = params["property"] as? [String : Any] else{
print("property信息不存在")
return
}
for key in propertyDic.keys{
//然后NSClassFromString获取到DataModel 的Class
let modelClassTemp : AnyClass? = NSClassFromString((nameSpace) + "." + key)
//然后判断有没有获取成功
guard let modleClsType = modelClassTemp as? NSObject.Type else{
return
}
//根据推送过来的信息对dataModel进行赋值
guard let modelDic = propertyDic[key] as? [String : String]else{
print("model信息不存在")
return
}
let dataModel = modleClsType.init()
for modelKey in modelDic.keys{
//首先确定model里面有相应的属性
if SwiftRunTimeTool.checkClassPerporty(object: object_getClass(dataModel), propertyStr: modelKey){
dataModel.setValue(modelDic[modelKey]! as String, forKey: modelKey)
}
}
//再根据推送过来dataModel再pushModel里面的名字字符串,对pushModel进行赋值
guard let modelName = params["modelName"] as? String else{
print("modelName不存在")
return
}
//确保pushModel里面有对应名字的属性
if SwiftRunTimeTool.checkClassPerporty(object: object_getClass(pushModel), propertyStr: modelName){
pushModel.setValue(dataModel, forKey: modelName)
}
}
//确保跳转的VC里面有pushModel属性
if SwiftRunTimeTool.checkClassPerporty(object: object_getClass(clsVC), propertyStr: "pushModel"){
clsVC.setValue(pushModel, forKey: "pushModel")
}
//找UITabBarController
guard let tabBarController = self.window?.rootViewController as?UITabBarController else {
print("UITabBarController找不到")
return
}
//找UINavigationController
guard let navigationController = tabBarController.viewControllers?[tabBarController.selectedIndex] as? UINavigationController else {
print("navigationController找不到")
return
}
//如果有Present上出的页面,可以通过 tabBarController调用Dismiss
// tabBarController.dismiss(animated: true, completion: nil)
clsVC.hidesBottomBarWhenPushed = true
navigationController.pushViewController(clsVC, animated: true)
}
好了,至此算是结束了,如果和后台对接好,理论上可以进行任意页面的跳转。。。最终Demo在这里:最终Demo