You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从UIView子类(SwiftMessages的MessageView)中以代码方式呈现ViewController

解决在Swift Messages的MessageView子类中呈现ViewController的问题

嘿,我懂你的需求啦——你在继承自MessageView的自定义类里加了个按钮,想通过它打开另一个ViewController对吧?因为MessageView是UIView的子类,本身没有直接present视图控制器的能力,我给你两种实用的解决方案:

方案1:获取当前顶层视图控制器来呈现

这种方法不需要依赖外部控制器,直接在按钮点击事件里找到当前活跃的顶层VC来跳转:

import Foundation
import SwiftMessages
import UIKit

class MyClass: MessageView {
    var hideBanner: (() -> Void)?
    
    @IBAction func helpButtonPressed(_ sender: UIButton) {
        // 1. 实例化目标ViewController(记得替换成你的VC标识符)
        let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
        guard let newViewController = storyBoard.instantiateViewController(withIdentifier: "HelpViewController") as? HelpViewController else {
            print("找不到对应的ViewController,请检查storyboard标识符")
            return
        }
        
        // 2. 获取当前顶层视图控制器
        guard let topViewController = UIApplication.shared.connectedScenes
                .filter({$0.activationState == .foregroundActive})
                .compactMap({$0 as? UIWindowScene})
                .first?.windows
                .filter({$0.isKeyWindow})
                .first?.rootViewController?.topMostViewController() else {
            print("无法获取顶层视图控制器")
            return
        }
        
        // 3. 呈现新页面(可按需设置modal样式)
        newViewController.modalPresentationStyle = .automatic
        topViewController.present(newViewController, animated: true)
        
        // 可选:同时隐藏当前的MessageView
        hideBanner?()
    }
}

// 给UIViewController加扩展,递归找到最顶层的VC(适配导航、TabBar控制器)
extension UIViewController {
    func topMostViewController() -> UIViewController {
        if let presented = presentedViewController {
            return presented.topMostViewController()
        }
        if let navigation = self as? UINavigationController {
            return navigation.visibleViewController?.topMostViewController() ?? navigation
        }
        if let tabBar = self as? UITabBarController {
            return tabBar.selectedViewController?.topMostViewController() ?? tabBar
        }
        return self
    }
}

方案2:用闭包回调让父控制器处理(更解耦)

如果想遵循单一职责原则,让自定义MessageView只负责展示,把跳转逻辑交给创建它的控制器,可以扩展闭包:

import Foundation
import SwiftMessages
import UIKit

class MyClass: MessageView {
    var hideBanner: (() -> Void)?
    // 添加回调闭包,通知外部要打开帮助页面
    var showHelpViewController: (() -> Void)?
    
    @IBAction func helpButtonPressed(_ sender: UIButton) {
        // 触发回调,让外部控制器处理跳转
        showHelpViewController?()
        // 可选:同时隐藏当前Banner
        hideBanner?()
    }
}

然后在创建MyClass的ViewController里设置闭包:

// 假设你在某个VC中创建并展示MyClass实例
let myMessageView = MyClass()
myMessageView.showHelpViewController = { [weak self] in
    guard let self = self else { return }
    let storyBoard = UIStoryboard(name: "Main", bundle: nil)
    guard let helpVC = storyBoard.instantiateViewController(withIdentifier: "HelpViewController") as? HelpViewController else { return }
    self.present(helpVC, animated: true)
}
// 展示MessageView
SwiftMessages.show(view: myMessageView)

注意事项

  • 一定要在Main.storyboard里给目标ViewController设置正确的Storyboard ID,要和代码里的标识符一致
  • 如果目标VC是导航控制器的一部分,也可以用pushViewController代替present,根据你的页面结构调整
  • 方案2里用[weak self]是为了避免循环引用,这一点很重要哦

内容的提问来源于stack exchange,提问作者Blisko

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 03:48:12