iOS应用中如何自定义Firebase应用内消息?含弹窗定制需求
iOS 自定义Firebase应用内消息(双按钮弹窗)实现方案
核心结论
可以通过Firebase In-App Messaging实现带标题、副标题及双按钮的自定义弹窗,核心是自定义消息显示库,拦截Firebase的消息回调,用自定义UI展示内容并处理按钮事件。
实现步骤
1. 基础环境配置
确保已集成Firebase核心SDK及In-App Messaging组件:
- 在
Podfile中添加依赖:pod 'Firebase/Core' pod 'Firebase/InAppMessagingDisplay' - 执行
pod install后,在AppDelegate中初始化Firebase:import Firebase func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { FirebaseApp.configure() return true }
2. 实现自定义弹窗UI
创建一个独立的ViewController作为弹窗容器,包含标题、副标题和两个操作按钮:
import UIKit class CustomInAppMessageVC: UIViewController { // MARK: UI Components private let titleLabel: UILabel = { let label = UILabel() label.font = .boldSystemFont(ofSize: 18) label.textAlignment = .center label.numberOfLines = 0 return label }() private let subtitleLabel: UILabel = { let label = UILabel() label.font = .systemFont(ofSize: 14) label.textAlignment = .center label.numberOfLines = 0 label.textColor = .gray return label }() private let primaryButton: UIButton = { let btn = UIButton(type: .system) btn.layer.cornerRadius = 8 btn.backgroundColor = .systemBlue btn.setTitleColor(.white, for: .normal) btn.titleLabel?.font = .systemFont(ofSize: 16, weight: .medium) return btn }() private let secondaryButton: UIButton = { let btn = UIButton(type: .system) btn.layer.cornerRadius = 8 btn.layer.borderWidth = 1 btn.layer.borderColor = UIColor.systemBlue.cgColor btn.setTitleColor(.systemBlue, for: .normal) btn.titleLabel?.font = .systemFont(ofSize: 16, weight: .medium) return btn }() // MARK: Callback Handlers var primaryActionHandler: (() -> Void)? var secondaryActionHandler: (() -> Void)? // MARK: Initializer init(title: String, subtitle: String, primaryBtnText: String, secondaryBtnText: String) { super.init(nibName: nil, bundle: nil) titleLabel.text = title subtitleLabel.text = subtitle primaryButton.setTitle(primaryBtnText, for: .normal) secondaryButton.setTitle(secondaryBtnText, for: .normal) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } // MARK: Lifecycle override func viewDidLoad() { super.viewDidLoad() setupUI() setupButtonActions() } // MARK: UI Setup private func setupUI() { view.backgroundColor = UIColor.black.withAlphaComponent(0.5) let containerView = UIView() containerView.backgroundColor = .white containerView.layer.cornerRadius = 12 containerView.clipsToBounds = true view.addSubview(containerView) containerView.translatesAutoresizingMaskIntoConstraints = false [titleLabel, subtitleLabel, primaryButton, secondaryButton].forEach { containerView.addSubview($0) $0.translatesAutoresizingMaskIntoConstraints = false } // Layout Constraints NSLayoutConstraint.activate([ containerView.centerXAnchor.constraint(equalTo: view.centerXAnchor), containerView.centerYAnchor.constraint(equalTo: view.centerYAnchor), containerView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.85), titleLabel.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 24), titleLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 16), titleLabel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -16), subtitleLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 12), subtitleLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 16), subtitleLabel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -16), secondaryButton.topAnchor.constraint(equalTo: subtitleLabel.bottomAnchor, constant: 24), secondaryButton.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 16), secondaryButton.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -16), secondaryButton.heightAnchor.constraint(equalToConstant: 44), primaryButton.topAnchor.constraint(equalTo: secondaryButton.bottomAnchor, constant: 12), primaryButton.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 16), primaryButton.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -16), primaryButton.heightAnchor.constraint(equalToConstant: 44), primaryButton.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: -24) ]) } // MARK: Button Actions private func setupButtonActions() { primaryButton.addTarget(self, action: #selector(primaryBtnTapped), for: .touchUpInside) secondaryButton.addTarget(self, action: #selector(secondaryBtnTapped), for: .touchUpInside) } @objc private func primaryBtnTapped() { primaryActionHandler?() dismiss(animated: true) } @objc private func secondaryBtnTapped() { secondaryActionHandler?() dismiss(animated: true) } }
3. 拦截Firebase消息并显示自定义弹窗
实现FIRInAppMessagingDisplayDelegate协议,拦截默认消息显示逻辑,解析自定义参数并展示弹窗:
import FirebaseInAppMessagingDisplay extension YourRootViewController: FIRInAppMessagingDisplayDelegate { func viewDidLoad() { super.viewDidLoad() // 设置代理拦截消息 FirebaseInAppMessagingDisplay.inAppMessagingDisplay().delegate = self } func messageDisplayWillDisplay(_ message: FIRInAppMessagingDisplayMessage) { // 解析Firebase消息中的自定义数据 guard let customData = message.customData else { return } let title = customData["title"] as? String ?? "" let subtitle = customData["subtitle"] as? String ?? "" let primaryBtnText = customData["primary_button_text"] as? String ?? "确认" let secondaryBtnText = customData["secondary_button_text"] as? String ?? "取消" let primaryAction = customData["primary_action"] as? String ?? "" // 实例化自定义弹窗 let messageVC = CustomInAppMessageVC( title: title, subtitle: subtitle, primaryBtnText: primaryBtnText, secondaryBtnText: secondaryBtnText ) // 配置按钮回调 messageVC.primaryActionHandler = { // 处理主按钮动作(比如跳转deeplink、触发业务逻辑) if let url = URL(string: primaryAction) { UIApplication.shared.open(url) } // 上报Firebase点击事件 FirebaseInAppMessaging.inAppMessaging().logEvent(.messageClicked, withMessage: message) } messageVC.secondaryActionHandler = { // 处理次要按钮动作 FirebaseInAppMessaging.inAppMessaging().logEvent(.messageDismissed, withMessage: message) } // 展示弹窗 messageVC.modalPresentationStyle = .overFullScreen messageVC.modalTransitionStyle = .crossDissolve present(messageVC, animated: true) } func messageDisplayDidDismiss(_ message: FIRInAppMessagingDisplayMessage) { // 可选:处理弹窗消失后的逻辑 } }
4. Firebase控制台配置自定义消息
在Firebase控制台创建应用内消息时:
- 选择自定义消息类型
- 在自定义数据模块添加以下键值对:
title: 弹窗标题内容subtitle: 弹窗副标题内容primary_button_text: 主按钮文字secondary_button_text: 次要按钮文字primary_action: 主按钮触发的动作(比如deeplink地址)
- 设置消息触发条件(如用户进入特定页面、满足特定用户属性等)
关键注意事项
- 必须设置
FIRInAppMessagingDisplayDelegate才能拦截默认UI,否则会显示Firebase原生弹窗 - 调用
logEvent上报事件,确保Firebase控制台能统计消息的点击/关闭数据 - 自定义弹窗的布局可根据需求调整(比如按钮横向排列、修改配色等)
- 测试时可使用Firebase的"测试消息"功能,直接推送自定义消息到测试设备
内容的提问来源于stack exchange,提问作者sejn
相关产品推荐
相关产品推荐

