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

如何不修改RatingView代码在UIKit中获取SwiftUI视图选中评分值

问题

现有如下已实现的SwiftUI评分视图RatingView,要求不改动RatingView任意一行代码,在UIKit视图控制器中获取用户选中的评分值。

原有代码如下:

struct RatingView: View {
    
    @Binding var rating: Int?
    
    private func starType(index: Int) -> String {
        
        if let rating = rating {
            return index <= rating ? "star.fill" : "star"
        } else {
            return "star"
        }
    }
    
    var body: some View {
        HStack {
            ForEach(1...5, id: \.self) { index in
                Image(systemName: self.starType(index: index))
                    .foregroundColor(Color.orange)
                    .onTapGesture {
                        rating = index
                }
            }
        }
    }
}

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.backgroundColor = .white
        
        // .constant(3) will be replaced by something in the ViewController so it can handle the changes for the rating
        let hostingController = UIHostingController(rootView: RatingView(rating: .constant(3)))
        
        guard let ratingsView = hostingController.view else { return }
        self.addChild(hostingController)
        
        self.view.addSubview(ratingsView)
}

解决方案

核心思路是利用SwiftUI Binding的自定义构造能力,不需要修改原RatingView的任何代码,只需要替换原本传入的.constant(3),自己实现一个双向绑定,在绑定值被写入时捕获评分变化即可。

Binding本质是对值的读写闭包封装,原视图内部对rating的赋值操作,最终会触发我们自定义的set逻辑,自然就能把用户选择的评分同步到UIKit侧。

修改后的ViewController实现如下:

class ViewController: UIViewController {
    // 存储当前选中的评分值,初始值与原代码保持一致为3
    private var selectedRating: Int? = 3

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        
        // 自定义双向绑定替换原来的.constant(3)
        let ratingBinding = Binding<Int?>(
            get: { [weak self] in
                self?.selectedRating
            },
            set: { [weak self] newValue in
                self?.selectedRating = newValue
                // 在此处执行评分变化后的业务逻辑,例如更新UI、上报数据等
                print("用户当前选中评分为:\(newValue ?? 0)")
            }
        )
        
        let hostingController = UIHostingController(rootView: RatingView(rating: ratingBinding))
        
        guard let ratingsView = hostingController.view else { return }
        addChild(hostingController)
        view.addSubview(ratingsView)
        
        // 补充布局约束,避免视图位置异常
        ratingsView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            ratingsView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 40),
            ratingsView.centerXAnchor.constraint(equalTo: view.centerXAnchor)
        ])
    }
}

实现说明:

  • 全程没有修改RatingView的任何代码,完全符合题目限制要求
  • 自定义Binding的get闭包负责把UIKit侧存储的评分值回传给SwiftUI视图,保证星星填充状态和选中值一致
  • set闭包会在用户点击星星触发rating = index赋值时自动调用,在这里即可拿到最新的评分值,执行任意UIKit侧的业务逻辑
  • 补充了必要的自动布局约束,将评分视图固定在安全区顶部居中位置,避免视图偏移

更新说明:题目原始代码来自社区公开讨论内容,本次实现未对原始RatingView代码做任何修改。

内容的提问来源于stack exchange,提问作者Mary Doe

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 08:03:29