如何在UIKit视图控制器中接收SwiftUI事件并实现双向通信
问题根源
StepperView是SwiftUI的结构体,属于值类型,你在ViewController中声明的stepView只是初始化时的静态副本,SwiftUI视图树后续更新的@State变量只会作用于视图树内的实例,和你外部存储的副本完全独立,所以调用getCount()永远只能拿到初始值1。- 你给外部的
stepView副本设置代理,并不会同步到视图树内正在运行的StepperView实例,所以代理方法永远不会触发。 - 额外的小问题:
@State属性的初始化方式错误,你在init中直接给count赋值不会生效,需要通过属性包装器的投影值(下划线前缀)初始化。
方案1:使用闭包回调实现双向交互(推荐,改造成本最低)
修改后的StepperView代码:
import SwiftUI struct StepperView: View { @State private var count: Int // 用闭包替代代理,更适配SwiftUI与UIKit的交互场景 var onCountChanged: ((Int) -> Void)? init(initialCount: Int) { // 正确的@State初始化方式 self._count = State(initialValue: initialCount) } var body: some View { HStack(alignment: .center, spacing: 10) { Button("+", action: { count += 1 print(count) onCountChanged?(count) }) Text("\(count)") .frame(minWidth: 40, minHeight: 40) .background(Color.white) .foregroundColor(.black) .scaledToFill() Button("-", action: { if count > 1 { count -= 1 print(count) } onCountChanged?(count) }) } .font(.system(size: 22, weight: .medium, design: .serif)) .frame(minWidth: 120, minHeight: 40, alignment: .center) .background(Color.blue) .foregroundColor(Color.white) .cornerRadius(20) } } struct Stepper_Previews: PreviewProvider { static var previews: some View { StepperView(initialCount: 1) .previewLayout(.fixed(width: 300, height: 70)) } }
修改后的ViewController代码:
import UIKit import SwiftUI class ViewController: UIViewController { @IBOutlet weak var stepperContenerView: UIView! @IBOutlet weak var btn: UIButton! // 本地存储最新的计数值,不需要再持有StepperView副本 private var currentStepperCount = 1 lazy var swiftUIHostingView: UIHostingController<StepperView> = { let stepView = StepperView(initialCount: 1) { [weak self] newCount in self?.currentStepperCount = newCount // 如果你需要实时更新UIKit控件的显示,在这里处理即可 // self?.lbl.text = "Stepper count is - \(newCount)" } return UIHostingController(rootView: stepView) }() override func viewDidLoad() { super.viewDidLoad() stepperContenerView.addSubview(swiftUIHostingView.view) addChild(swiftUIHostingView) swiftUIHostingView.view.frame = stepperContenerView.bounds stepperContenerView.addConstrained(subview: swiftUIHostingView.view) swiftUIHostingView.didMove(toParent: self) } @IBAction func onClickBtn(_ sender: UIButton) { let alert = UIAlertController(title: "Stepper count", message: "Current value : \(currentStepperCount)", preferredStyle: .alert) alert.addAction(UIAlertAction(title: "ok", style: .default, handler: nil)) present(alert, animated: true) } }
方案2:使用ObservableObject做状态共享(适合复杂交互场景)
如果后续交互逻辑更复杂,可以抽离独立的ViewModel共享状态:
// 共享的ViewModel class StepperViewModel: ObservableObject { @Published var count = 1 }
StepperView内使用@ObservedObject var viewModel: StepperViewModel来读写count,ViewController持有ViewModel实例,直接监听viewModel.$count的变化或者直接读取viewModel.count就能拿到最新值。
内容的提问来源于stack exchange,提问作者Abhishek Kapoor
相关产品推荐
相关产品推荐

