如何防止被推入的SFSafariViewController关闭UITabBarController?
解决SFSafariViewController推入UITabBarController后,点击Done关闭整个TabBar的问题
这个问题我做项目时也踩过坑,核心原因是SFSafariViewController的Done按钮默认会触发导航栈的pop操作,如果你的UITabBarController本身就在导航栈里,就会被连带pop掉,导致整个TabBar消失。这里给你几个实用的解决思路:
方法一:用present替代push展示SafariVC(最推荐)
如果你的业务场景不需要把Safari页面嵌入到TabBar的导航层级里,直接用present的方式弹出SFSafariViewController是最稳妥的。点击Done按钮时,只会dismiss当前的Safari控制器,完全不会影响UITabBarController的存在。
代码示例:
import SafariServices // 在你的TabBar子控制器里调用 let targetURL = URL(string: "https://example.com")! let safariVC = SFSafariViewController(url: targetURL) safariVC.delegate = self // 可选,如果你需要监听Done按钮事件 present(safariVC, animated: true)
方法二:把SafariVC包装在容器ViewController中(适合必须push的场景)
如果业务要求必须把Safari页面push到导航栈里,那可以创建一个普通的UIViewController作为容器,把SFSafariViewController作为子控制器添加进去。这样push的是容器VC,点击Done按钮时,只会pop这个容器VC,不会触动UITabBarController。
代码示例:
import SafariServices class SafariContainerViewController: UIViewController, SFSafariViewControllerDelegate { private var safariVC: SFSafariViewController! init(targetURL: URL) { super.init(nibName: nil, bundle: nil) safariVC = SFSafariViewController(url: targetURL) safariVC.delegate = self } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewDidLoad() { super.viewDidLoad() // 将SafariVC添加为子控制器 addChild(safariVC) safariVC.view.frame = view.bounds view.addSubview(safariVC.view) safariVC.didMove(toParent: self) } // 监听Done按钮,手动pop容器VC func safariViewControllerDidFinish(_ controller: SFSafariViewController) { navigationController?.popViewController(animated: true) } } // 在需要push的地方调用 let containerVC = SafariContainerViewController(targetURL: targetURL) navigationController?.pushViewController(containerVC, animated: true)
方法三:自定义导航控制器拦截pop操作(不推荐,易踩坑)
如果以上两种方法都不适用,你可以尝试自定义导航控制器的delegate,拦截Done按钮触发的pop操作,确保只移除SFSafariViewController而不是整个TabBar。不过这种方法要注意边界情况(比如用户滑动返回的逻辑),容易引发导航栈的异常,所以仅作为备选方案。
代码示例:
class CustomNavigationController: UINavigationController, UINavigationControllerDelegate { override func viewDidLoad() { super.viewDidLoad() delegate = self } func navigationController(_ navigationController: UINavigationController, shouldPop viewController: UIViewController) -> Bool { // 当要pop的是SafariVC时,手动执行pop并返回false阻止默认行为 if viewController is SFSafariViewController { popViewController(animated: true) return false } // 其他情况保持默认逻辑 return true } }
整体来说,优先选择方法一或方法二,既能解决问题又能保证导航逻辑的稳定性。
内容的提问来源于stack exchange,提问作者Peter Balogh
相关产品推荐
相关产品推荐

