现在都2025年了,写UIKit写的烦。
那堆addSubView、makeConstraint写得整个人都红温了,就老想着用SwiftUI来写,可是又离不开当前的UIViewController的框架。
于是乎试了一下在UIViewController里面写SwiftUI,第一个想到的就是用UIHostingController:
class MyViewController:
override func viewDidLoad() {
super.viewDidLoad()
let hostingController = UIHostingController(rootView: MySwiftUIView("blablabla")
addChild(hostingController)
hostingController.didMove(toParent: self)
view.addSubview(hostingController.view)
hostingController.view.frame = view.bounds
hostingController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
}
}
这样写很不灵活,每次都要创建UIHostingController、addSubview、addChild、setFrame之类的,照样很烦躁。
发现UIHostingController有个rootView属性,可以随地修改里面的SwiftUIView。嗯,将这个MyViewController改一下封装成一个父类,然后子类重写hostingController.rootView可以不?
class MyViewController: UIViewController {
var hostingController: UIHostingController!
// viewdidLoad同上省略
}
问题出现了!UIHostingController是带泛型Content的,Content遵循View协议!必须给定一个固定的类型。可是这样子就又有问题,我根本不知道我Content会传什么类型的进去,子类重写的话可能还要变成另一种类型。
想到了!借用AnyView擦除类型
改一下
class MyViewController: UIViewController {
var hostingController: UIHostingController<AnyView>!
// viewdidLoad同上省略
}
// 子类可以这样写了
class TestVC: MyViewController {
override func viewDidLoad() {
super.viewDidLoad()
hostingController.rootView = AnyView(
MySwiftUIView("代码blablabla")
)
}
}
哈哈哈,天才!
但是,每次子类改rootView都要用AnyView包一下,而且还是有括号()的,太丑了。
有了!父类加个方法,专门用来修改rootView,而且支持泛型:
class MyViewController: UIViewController {
var hostingController: UIHostingController<AnyView>!
// viewdidLoad内容省略
func setRootView<Content>(@ViewBuilder content: () -> Content) where Content: View {
let c = content()
hostingController.rootView = AnyView(c)
}
}
// 子类可以这样写了
class TestVC: MyViewController {
override func viewDidLoad() {
super.viewDidLoad()
setRootView {
MySwiftUIView("代码blablabla")
}
}
}
暂时看起来可以了,不用关心UIHostingController的Content是什么类型,子类随便写。
未完待续。。。