UIKit中如何让UIView始终保持在最上层?
解决UIKit中Toast在present控制器时被遮挡的问题
问题根源
iOS 13+引入多场景机制后,UIApplication.shared.windows.last 不一定能获取到当前活跃的主窗口;另外,present全屏控制器时,系统会将该控制器的视图层级置于原有窗口内容之上,导致添加到旧窗口的Toast被遮挡。
解决方案一:获取当前活跃场景的Key Window并置顶Toast
直接获取当前处于前台活跃状态的场景对应的Key Window,将Toast添加到该窗口,并确保Toast始终处于层级最上层:
// 获取当前活跃场景的Key Window guard let activeScene = UIApplication.shared.connectedScenes.first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene, let keyWindow = activeScene.windows.first(where: { $0.isKeyWindow }) else { return } // 添加Toast到Key Window keyWindow.addSubview(toastView) // 确保Toast在最上层 keyWindow.bringSubviewToFront(toastView)
解决方案二:使用独立的高层级Window承载Toast
创建一个专门的Window,将其层级设置为高于系统弹窗(如Alert),这样无论present任何控制器,Toast都会保持在最上层:
class ToastWindow: UIWindow { override init(frame: CGRect) { super.init(frame: frame) // 设置层级高于系统Alert windowLevel = UIWindow.Level.alert + 1 // 背景透明不影响下层内容 backgroundColor = .clear // 不拦截下层视图的触摸事件 isUserInteractionEnabled = false // 必须设置rootViewController,否则可能导致布局异常 rootViewController = UIViewController() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } // 显示Toast时的用法 let toastWindow = ToastWindow(frame: UIScreen.main.bounds) toastWindow.makeKeyAndVisible() toastWindow.addSubview(toastView) // Toast消失后记得销毁Window,避免内存泄漏 DispatchQueue.main.asyncAfter(deadline: .now() + 2) { toastWindow.isHidden = true toastWindow.removeFromSuperview() }
额外注意事项
- 如果使用独立Window方案,务必在Toast消失后隐藏并移除该Window,防止内存泄漏。
- 若Toast需要支持交互(如点击消失),可将
isUserInteractionEnabled设为true,但需处理好事件传递。
内容的提问来源于stack exchange,提问作者KimJitae
相关产品推荐
相关产品推荐

