通过UIHostingController传递给SwiftUI视图的函数无法执行?
问题原因及解决方案
错误根源分析
你遇到的NSCFString countByEnumeratingWithState:objects:count:错误,主要来自两个核心问题:
1. UIKit侧函数传递的作用域顺序错误
在loadInitialScreen()方法中,你先创建MessageView并引用action(),但action()函数的定义却在这之后。Swift中函数作用域是自上而下生效的,此时闭包引用action()时,该函数还未完成定义,会引发运行时的引用异常。
2. SwiftUI侧视图交互嵌套冲突
你将带Markdown链接的Text嵌套在Button内部,同时给Text添加了onOpenURL回调。这种嵌套会导致点击链接时,系统同时触发Button的点击事件和onOpenURL回调,Markdown链接的解析逻辑还可能和Button的交互逻辑冲突,引发类型识别错误(把字符串误当作集合类型处理)。
修复步骤
步骤1:修正UIKit侧的函数定义与传递
将action()函数的定义移到MessageView创建之前,或者直接传递函数引用,避免作用域问题:
class RootViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() self.loadInitialScreen() } func loadInitialScreen() { // 先定义action函数,再创建MessageView func action() { print("ACTION PRESSED") } let messageViewController = UIHostingController(rootView: MessageView(messageTitle: "Title", action: action) // 直接传递函数引用 ) self.navigationController?.pushViewController(messageViewController, animated: false) } }
如果要把action()定义在方法外部,直接传递self.action即可:
class RootViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() self.loadInitialScreen() } func action() { print("ACTION PRESSED") } func loadInitialScreen() { let messageViewController = UIHostingController(rootView: MessageView(messageTitle: "Title", action: self.action) ) self.navigationController?.pushViewController(messageViewController, animated: false) } }
步骤2:修复SwiftUI侧的交互逻辑
移除Button和Text的嵌套,选择单一交互方式:
- 如果想用链接触发动作,直接使用带
onOpenURL的Markdown Text:
struct MessageView: View { let messageTitle: String let action: () -> Void var body: some View { HStack { Text("[\(messageTitle)](refresh://action)") + Text(" Text") } .onOpenURL { _ in action() } } }
- 如果想用Button触发,直接去掉Markdown链接:
struct MessageView: View { let messageTitle: String let action: () -> Void var body: some View { Button(action: action) { HStack { Text(messageTitle) + Text(" Text") } } } }
额外说明
如果确实需要同时支持按钮点击和URL跳转,建议分开布局,避免交互嵌套导致的逻辑冲突。
内容的提问来源于stack exchange,提问作者Jake Smith
相关产品推荐
相关产品推荐

