添加至UIWindow的UIView在Presented视图控制器中不可见,求解决方案
解决子视图在Presented控制器上不显示的问题
先修正原代码的内存问题
原代码中mySubView是局部变量,虽添加到window后会被持有,但后续无法全局访问调整层级,先改成全局持有:
// 全局持有自定义子视图,方便后续操作 var globalMySubView: MySubView? func showSubViewInAllScreens() { let mySubView = MySubView() // 根据需求设置子视图frame,示例为全屏 mySubView.frame = UIScreen.main.bounds guard let window = UIApplication.shared.windows.last else { return } window.addSubview(mySubView) globalMySubView = mySubView }
方案一:Hook Present方法自动调整层级
通过Method Swizzling拦截所有视图控制器的present操作,完成后自动将子视图置于顶层:
extension UIViewController { static let swizzlePresentHandler: Void = { let originalSelector = #selector(present(_:animated:completion:)) let swizzledSelector = #selector(swizzledPresent(_:animated:completion:)) guard let originalMethod = class_getInstanceMethod(Self.self, originalSelector), let swizzledMethod = class_getInstanceMethod(Self.self, swizzledSelector) else { return } method_exchangeImplementations(originalMethod, swizzledMethod) }() @objc private func swizzledPresent(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) { // 执行原present逻辑 swizzledPresent(viewControllerToPresent, animated: flag, completion: { // Present完成后,将子视图前置 if let subView = globalMySubView, let window = UIApplication.shared.windows.last { window.bringSubviewToFront(subView) } completion?() }) } }
在App启动时初始化Swizzle:
// 在AppDelegate的didFinishLaunchingWithOptions里添加 func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { UIViewController.swizzlePresentHandler return true }
方案二:监听Window的子视图添加事件
通过系统通知监听window新增子视图的操作,判断是全屏presented视图后,自动调整层级:
func setupSubViewLevelObserver() { NotificationCenter.default.addObserver(forName: UIWindow.didAddSubviewNotification, object: nil, queue: .main) { notification in guard let addedView = notification.object as? UIView, let targetSubView = globalMySubView, addedView != targetSubView else { return } // 全屏视图大概率是presented控制器的view if addedView.frame == UIScreen.main.bounds { addedView.window?.bringSubviewToFront(targetSubView) } } }
调用showSubViewInAllScreens()后,执行此方法添加监听即可。
方案三:子视图主动维护层级(最简方案)
在自定义子视图的layoutSubviews方法中,主动将自己置于父视图顶层,适合简单场景:
class MySubView: UIView { override func layoutSubviews() { super.layoutSubviews() // 每次布局时确保自己在最上层 self.superview?.bringSubviewToFront(self) } }
这个方案无需额外监听或Hook,仅需修改自定义子视图代码,性能影响可忽略。
内容的提问来源于stack exchange,提问作者Arunachalam Ganesan
相关产品推荐
相关产品推荐

