问题描述
笔者在App登录后,切换RootViewController时,发现window上盖着一层看不见的View。如图所示:
然后看了window的层级,如图所示:
发现window除了有一个根控制器TabbarController,还有一个UILayoutContainerView,不难分析出,这就是之前旧的RootVC残留下来,没有释放的View。
解决方法
一开始以循环引用的角度入手,但是无奈项目过于庞大,找了非常久仍无法解决问题。最后只好从网上寻求解决方式,最后找到了跟我遇到一样问题的一篇,文章,链接如下:
http://openfibers.github.io/blog/2015/12/15/window-setrootviewcontroller-view-not-removed-hack/
文中的解决如下:
//hack of setRootViewController: old rootViewController's view never removed from window
- (void)setRootViewController:(UIViewController *)rootViewController
{
//remove old rootViewController's sub views
for (UIView* subView in self.rootViewController.view.subviews)
{
[subView removeFromSuperview];
}
//remove old rootViewController's view
[self.rootViewController.view removeFromSuperview];
//set new rootViewController
[super setRootViewController:rootViewController];
//remove empty UILayoutContainerView(s) remaining on root window
for (UIView *subView in self.subviews)
{
if (subView.subviews.count == 0)
{
[subView removeFromSuperview];
}
}
}
由于项目是用swift编写的,然后兴冲冲的将以上代码翻译成swift语言,发现怎样都不能满足 subView.subviews.count == 0
。经过打印子view的层级结构,发现UILayoutContainerView仍残余 UITransitionView,那么这个就是 subView.subviews.count == 0
不满足的原因。因此,我们只要耐心等专场动画结束后,再去判断 subView.subviews.count == 0
即可。修改后的代码如下:
class MPWindow: UIWindow {
override var rootViewController: UIViewController? {
willSet {
guard let old = rootViewController else {
return
}
for sub in old.view.subviews {
sub.removeFromSuperview()
}
old.view.removeFromSuperview()
}
didSet {
// 延迟2s判断
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Double((Int64)(2 * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)) { () -> Void in
for v in self.subviews {
if v.subviews.count == 0 {
v.removeFromSuperview()
}
}
}
}
}
}
网友评论