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

通过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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 07:30:48