使用 storyboard 的时候,我们经常会写出下面这样的代码,用来跳转到其他页面
if let editVC = self.storyboard?.instantiateViewController(withIdentifier: "EditVC") as? EditVC {
self.navigationController?.pushViewController(editVC, animated: true)
}
最多改成这样
guard let editVC = self.storyboard?.instantiateViewController(withIdentifier: "EditVC") as? EditVC else { return }
实在是太冗长了,简化第一步,或许我们可以直接将 <code>EditVC</code> 这个类型字符串化变成 <code>"EditVC"</code>,代码变成:
guard let editVC = self.storyboard?.instantiateViewController(withIdentifier: String(describing: EditVC.self)) as? EditVC else { return }
嗯,更长了,但是既然 <code>EditVC</code> 使用了两次,这使得我们有机会把这个过程封装成一个函数,而 <code>EditVC</code> 是唯一需要传递的参数
func instantiateVC(vc:UIViewController) -> UIViewController? {
let id = String(describing: vc.self)
return storyboard?.instantiateViewController(withIdentifier: id)
}
// how to use
guard let editVC = instantiateVC(vc: EditVC()) as? EditVC else { return }
封装之后依然尴尬:
- 因为我们不能确定每次都需要函数返回 <code>EditVC</code> ,所以只能用 <code>UIViewController</code> 当做返回值,这使得我们还需要再加上 <code>as? EditVC</code> 去做类型转换,相当于还是手动使用了两次 <code>EditVC</code> ,而不是一次
- 传入的参数是 <code>EditVC()</code> 而不是 <code>EditVC</code> ,看起来丑陋
一次解决两个问题的答案是:泛型!
我们在函数中限定泛型 <code>T</code> 为 <code>UIViewController</code> ,但 <code>T</code> 具体是我们的 App 中的哪个子类我们不去管,通过函数的参数来指定 T 的具体类型,随后确定出我们函数的返回值为那个我们指定的 <code>T</code>
最终结果:
extension UIViewController {
func instantiateVC<T: UIViewController>(type: T.Type) -> T? {
let id = String(describing: T.self)
return storyboard?.instantiateViewController(withIdentifier: id) as? T
}
}
// how to use
guard let editVC = instantiateVC(type: EditVC.self) else { return }
前后对比:
// before
guard let editVC = storyboard?.instantiateViewController(withIdentifier: "EditVC") as? EditVC else { return }
// after
guard let editVC = instantiateVC(type: EditVC.self) else { return }
提醒:
这里我假装这个函数回避掉了直接使用字符串去传入 <code>Storyboard ID</code> 的风险,但可以这么做的前提是,这里的 <code>EditVC</code> 到 <code>"EditVC"</code> 可以直接字符串化。
如果你也要这么做的话,至少要保证你的类名的字符串化的结果和你的 <code>Storyboard ID</code> 是有固定关联的。比如 <code>EditVC</code> 对应 <code>"editvc"</code> 或 <code>"EDITVC"</code> 或 <code>"editVC"</code> (这样你只要修改字母大小写就可以了),而不是 <code>"abc123"</code> 这种随便写的东西。