iOS13及以上非全屏模态视图下Firebase自定义屏幕名追踪异常问题
最优解决方案:自定义视图控制器基类+全局追踪管理器
针对iOS 13+非全屏模态关闭时父视图viewWillAppear不触发导致的手动屏幕追踪错误问题,结合你需要自定义屏幕名、无法使用Firebase自动追踪的需求,推荐以下低耦合、易维护的方案,同时先分析你现有思路的利弊:
现有思路分析
- 思路1:设所有模态为全屏:直接排除,不符合产品需求。
- 思路2:模态视图加代理告知父视图:可行但耦合度高,若应用中有大量模态视图,每个都要编写代理逻辑,后期维护成本高。
- 思路3:每个事件单独设屏幕名:可行但工作量极大,容易遗漏事件,且重复代码多,不符合代码复用原则。
推荐方案:自定义基类+全局追踪管理器
核心思路是模仿Firebase的底层逻辑——维护当前活跃的视图控制器栈,在模态视图(无论手动下滑还是代码关闭)消失时,自动切换到栈顶的父视图并触发屏幕名上报,同时完全支持自定义屏幕名。
步骤1:创建全局屏幕追踪管理器
class ScreenTrackingManager { static let shared = ScreenTrackingManager() private var activeVCStack: [UIViewController] = [] // 替换成你实际的屏幕上报逻辑 private func trackScreen(named screenName: String) { // 示例:Firebase事件上报 // Analytics.logEvent(AnalyticsEventScreenView, parameters: [AnalyticsParameterScreenName: screenName]) print("上报屏幕:\(screenName)") } // 更新活跃视图控制器栈,并触发追踪 func updateActiveVC(_ vc: UIViewController, isAppearing: Bool) { if isAppearing { // 移除栈中重复的控制器,避免重复上报 activeVCStack.removeAll { $0 == vc } activeVCStack.append(vc) // 上报当前控制器的自定义屏幕名 if let trackingVC = vc as? TrackingViewController { trackScreen(named: trackingVC.customScreenName) } } else { // 从栈中移除当前控制器 activeVCStack.removeAll { $0 == vc } // 获取新的栈顶控制器,触发上报 if let topVC = activeVCStack.last as? TrackingViewController { trackScreen(named: topVC.customScreenName) } } } }
步骤2:自定义视图控制器基类
让所有需要追踪屏幕的视图控制器继承这个基类,统一处理生命周期监听和模态关闭场景:
class TrackingViewController: UIViewController { // 子类必须重写该属性,设置自定义屏幕名 var customScreenName: String { fatalError("子类必须重写customScreenName属性") } override func viewDidLoad() { super.viewDidLoad() // 监听手动下滑关闭模态的场景 presentationController?.delegate = self } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) ScreenTrackingManager.shared.updateActiveVC(self, isAppearing: true) } override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) ScreenTrackingManager.shared.updateActiveVC(self, isAppearing: false) } override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) { super.dismiss(animated: flag) { // 代码关闭模态时,触发栈更新 ScreenTrackingManager.shared.updateActiveVC(self, isAppearing: false) completion?() } } } // 处理手动下滑关闭模态的场景 extension TrackingViewController: UIAdaptivePresentationControllerDelegate { func presentationControllerDidDismiss(_ presentationController: UIPresentationController) { ScreenTrackingManager.shared.updateActiveVC(self, isAppearing: false) } }
步骤3:子类使用示例
class HomeViewController: TrackingViewController { override var customScreenName: String { return "首页" } // 弹出非全屏模态视图 func showSettingsModal() { let modalVC = SettingsModalViewController() modalVC.modalPresentationStyle = .pageSheet // 非全屏模态样式 present(modalVC, animated: true) } } class SettingsModalViewController: TrackingViewController { override var customScreenName: String { return "设置弹窗" } }
方案优势
- 低耦合:所有追踪逻辑集中在基类和管理器中,子类只需设置自定义屏幕名即可,无需重复编写代理或通知逻辑。
- 覆盖全场景:无论是手动下滑关闭模态,还是代码调用
dismiss,都会自动触发父视图的屏幕名更新。 - 易维护:后续新增需要追踪的视图控制器,只需继承
TrackingViewController并设置屏幕名即可。 - 适配导航栈:导航控制器的push/pop操作会自动触发
viewDidAppear/viewDidDisappear,同样能正确更新屏幕追踪。
内容的提问来源于stack exchange,提问作者anoop4real
相关产品推荐
相关产品推荐

