iOS可以通过在 UIViewController 内部增加子 UIViewController 来管理界面,这样可重用的界面管理起来更方便,而且增加的可重用界面还能有 UIViewController 生命周期的回调,方便增加相应的事件处理,还是很方便的。
增加子视图控制器如下:
addChild(child)
child.view.frame = frame
view.addSubview(child.view)
child.didMove(toParent: self)
移除子控制器:
willMove(toParent: nil)
view.removeFromSuperview()
removeFromParent()
为了方便使用可以对 UIViewController 增加扩展。
extension UIViewController {
func add(_ child: UIViewController) {
addChild(child)
view.addSubview(child.view)
child.didMove(toParent: self)
}
func remove() {
guard parent != nil else {
return
}
willMove(toParent: nil)
view.removeFromSuperview()
removeFromParent()
}
}
举个例子,例如加载完成之前需要显示一个无数据的loading提示:
定义一个 LoadingViewController
class LoadingViewController: UIViewController {
// 由于能够获取到 ViewController 的生命周期回调,可以搞一些事情
override func viewDidLoad() {
super.viewDidLoad()
let framesize = view.frame.size
let l = UILabel.init(frame: CGRect.init(x: (framesize.width - 100)/2, y: (framesize.height - 100)/2, width: 100, height: 100))
l.text = "loading"
l.tintColor = .white
view.addSubview(l)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
}
}
定一个测试主 ViewController :
class MainViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// ... 绘制其他的界面数据
// 把此部分放在页面的最后,以便显示在最上方
let loadingVC = LoadingViewController()
add(loadingVC)
// 模拟加载数据
let t = DispatchTime.now() + 10
DispatchQueue.main.asyncAfter(deadline: t) {
loadingVC.remove()
}
}
}
可以发现如果不指定 LoadingViewController 的 frame 的话,其页面大小是填充父视图控制器的,比较方便。
可以想想,例如骨架屏,提示,弹窗之类的都可以采用类似的实现来操作,而且页面相对独立,方便代码逻辑实现。
例子中把子视图控制器的页面增加在父视图控制器的页面上,其实还能够增加到一个指定的视图上,用来做一些特殊的操作。