如何从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
相关产品推荐
相关产品推荐

