Swift3.1-根据类名动态创建类

    xiaoxiao2021-03-25  91

    什么是命名空间(namespace) 通常来说,命名空间是唯一识别的一套名字,这样当对象来自不同的地方但是名字相同的时候就不会含糊不清了。 为什么需要命名空间(namespace) Objective-C 没有命名空间的,在应用开发时,所有的代码和引用的静态库最终都会被编译到同一个域和二进制中。这样的后果是一旦我们有重复的类名的话,就会导致编译时的冲突和失败。为了避免这种事情的发生,Objective-C的类型一般都会加上两到三个字母的前缀,比如 Apple保留的 NS UI前缀,各个系统框架的前缀 SK (StoreKit)CG (CoreGraphic)等。Objective-C社区的大部分开发者也遵守了这个约定,一般都会将自己名字缩写作为前缀,把类库命名为AFNetworking或者MBProgressHUD这样。这种做法可以解决部分问题,至少我们在直接引用不同人的库时冲突的概率大大降低了,但是前缀并不意味着不会冲突,有时候我们确实还是会遇到即使使用前缀也仍然相同的情况。另外一种情况是可能你想使用的两个不同的库,分别在它们里面引用了另一个相同的很流行的第三方库,而又没有更改名字。在你分别使用这两个库中的一个时是没有问题的,但是一旦你将这两个库同时加到你的项目中的话,这个大家共用的第三方库就会和自己发生冲突了。 Swift 中,由于可以使用命名空间了,即使是名字相同的类型,只要是来自不同的命名空间的话,都是可以和平共处的。swift中的命名空间的使用不是一个项目,而是需要跨项目,在一个项目中,都是一个命名空间,在同一个命名空间下,所有全局变量或者函数共享,不需要import,swift开始,官方更多的建议大家使用pod来管理第三方框架。 Swift中是实现了命名空间功能的,只是这个命名空间不像C#namespace或者Java中的package那样需要显式在文件中指定,而是采用模块(Module)的概念:在同一个模块中所有的Swift类处于同一个命名空间,它们之间不需要导入就可以相互访问。很明显Swift的这种做法是为了最大限度的简化Swift编程。其实一个module就可以看成是一个project中的一个target,在创建项目的时候默认就会创建一个target,这个target的默认模块名称就是这个项目的名称(可以在targetBuild Settings—Product Module Name配置) 注意几点: 1:不同项目中的命名空间是不一样的, 默认情况下命名空间的名称就是当前项目的名称 2:因为Swift可以通过命名空间来解决重名的问题,所以在做Swift开发时尽量使用cocoapods来集成三方框架,这样可以有效的避免类名重复 3:因为Swift中有命名空间,所以通过一个字符串来创建一个类和OC中也不太一样了, OC中可以直接通过类名创建一个类,Swift中如果想通过类名来创建一个类必须加上命名空间。 swift 中,类名的组成格式是 namespace.类名,如下图代码打印所示: 怎么查看命名空间(namespace) namespaceinfo.plist 对应的是CFBundleExecutable,我们可以在info.plist中任意右击一行,选中Show Raw Keys/Values查看真实的key,如下2所示: 如何修改命名空间(namespace)     如果新建项目时,项目名称中包含有中文,可以进入是Build Settings中选中"All",搜索product name,即可修改,如图:

    如何代码获取命名空间名,并动态创建相应的类

    //1:动态获取命名空间 guard let name = Bundle.main.infoDictionary!["CFBundleExecutable"] as? String else { SHLog(message: "获取命名空间失败") return } let cls: AnyClass? = NSClassFromString(name + "." + VCName) //VCName:表示试图控制器的类名 // Swift中如果想通过一个Class来创建一个对象, 必须告诉系统这个Class的确切类型 guard let typeClass = cls as? UIViewController.Type else { SHLog(message: "cls不能当做UIViewController") return } let childController = typeClass.init()

    根据上面的内容,下面看一个具体的例子

    动态的显示UITabBar上的内容,包括图片、文字,所谓动态是指根据服务端返回的数据,动态显示底部UITabBar上的图片和文字,这里我们使用本地json文件进行模仿服务器返回的json数据,实现根据本地json文件加载显示对应的图片和内容,实现动态化设置,主体思路就是将子视图控制器的类名,显示的图片名称,title使用字典存储,使用数组存储所有相关字典,我们只需要拿到JSON文件并进行解析获得对应数据即可。注意一点,如果服务器没有返回JSON数据,那么使用默认数据。

    首先看一下使用JSON文件和默认数据的显示效果图:

    具体代码如下:

    class MainViewController: UITabBarController { override func viewDidLoad() { super.viewDidLoad() //ios7以后,只需要设置tintColor,那么图片和文字都会按照tintColor进行渲染 tabBar.tintColor = UIColor.orange //添加子控制器 addChildViewControllers() } }

    添加所有子控制器方法中,实现了读取本地JSON数据、将JSON数据转换为对象(数组字典)、遍历数组字典取出每一个字典

    func addChildViewControllers() { //1:读取JSON数据 guard let filePath = Bundle.main.path(forResource: "MainVCSettings.json", ofType: nil) else { SHLog(message: "JSON文件不存在") return } guard let data = NSData(contentsOfFile: filePath) else { SHLog(message: "加载二进制数据失败") return } //2:将JSON数据转换为对象(数组字典) do { let dicts = try JSONSerialization.jsonObject(with: data as Data, options: JSONSerialization.ReadingOptions.mutableContainers) as! [[String: AnyObject]] //3:遍历数组字典取出每一个字典 for dict in dicts { let title = dict["title"] as? String let vcName = dict["vcName"] as? String let imageName = dict["imageName"] as? String addChildViewController(vcName: vcName, title: title, imageName: imageName) } } catch { // 只要try对应的方法发生了异常, 就会执行catch{}中的代码,即使用默认数据 addChildViewController(vcName: "HomeTableViewController", title: "首页", imageName: "tabbar_home") addChildViewController(vcName: "MessageTableViewController", title: "消息", imageName: "tabbar_message_center") addChildViewController(vcName: "DiscoverTableViewController", title: "发现", imageName: "tabbar_discover") addChildViewController(vcName: "ProfileTableViewController", title: "我", imageName: "tabbar_profile") } }

    addChildViewController方法实现如下:

    func addChildViewController(vcName: String?, title: String?, imageName: String?) { //1:动态获取命名空间,开发中应该充分利用guard语句,guard可以有效的解决可选绑定容易形成{}嵌套问题 guard let name = Bundle.main.infoDictionary!["CFBundleExecutable"] as? String else { SHLog(message: "获取命名空间失败") return } //2:根据字符串获取Class var cls: AnyClass? = nil if let controllerName = vcName{ cls = NSClassFromString(name + "." + controllerName) } //Swift中如果想通过一个Class来创建一个对象, 必须告诉系统这个Class的确切类型,这里注意:所有的视图控制器都是继承于UITableViewController guard let typeClass = cls as? UITableViewController.Type else { SHLog(message: "cls不能当做UITableViewController") return } //通过Class创建对象 let childController = typeClass.init() //3:设置控制器相关属性 childController.title = title if let imageName = imageName { //从内像外设置, nav和tabbar都有 childController.tabBarItem.image = UIImage(named: imageName) childController.tabBarItem.selectedImage = UIImage(named: "\(imageName)_highlighted") } //4:包装一个导航控制器 let nav = UINavigationController(rootViewController: childController) //5:添加子控制器到UITabBarController addChildViewController(nav) } JSON文件内容如下图:

    参考:

    命名空间 - Swift必备tips

    命名空间

    转载请注明原文地址: https://ju.6miu.com/read-15949.html

    最新回复(0)