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

如何在UIKit视图控制器中接收SwiftUI事件并实现双向通信

问题根源

  1. StepperView 是SwiftUI的结构体,属于值类型,你在ViewController中声明的stepView只是初始化时的静态副本,SwiftUI视图树后续更新的@State变量只会作用于视图树内的实例,和你外部存储的副本完全独立,所以调用getCount()永远只能拿到初始值1。
  2. 你给外部的stepView副本设置代理,并不会同步到视图树内正在运行的StepperView实例,所以代理方法永远不会触发。
  3. 额外的小问题:@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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.23 20:54:00