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

UIKit集成SwiftUI:点击UIKit按钮无法更新SwiftUI视图选中状态

问题解决:UIKit嵌入SwiftUI后无法更新选中状态

问题根源

你遇到的视图不更新问题,核心原因有三个:

  1. 值类型数组的更新机制限制:PresetBank是struct值类型,修改数组内元素的属性时,数组本身的引用未发生变化,@Published只会在数组被整体替换时才会发送视图更新通知。
  2. ObservableObject的监听方式错误:ContentView中用@State存储PresetStore(ObservableObject),无法正确监听数据变化。
  3. 冗余状态变量:PresetBankView中的@State var isSelected完全未被使用,属于无效代码。

修复步骤及代码修改

1. 修正PresetStore的选中逻辑

修改selectBank方法,通过创建新数组的方式触发@Published的更新通知:

class PresetStore: ObservableObject {
    @Published var arrayOfPresets: [PresetBank]
    
    init(arrayOfPresets: [PresetBank]) {
        self.arrayOfPresets = arrayOfPresets
    }
    
    public func selectBank(index: Int) {
        print(index)
        // 通过map创建新数组,修改选中状态后重新赋值
        arrayOfPresets = arrayOfPresets.enumerated().map { idx, bank in
            var updatedBank = bank
            updatedBank.isSelected = idx == index
            return updatedBank
        }
    }
}

2. 修正ContentView的数据源监听

将@State替换为@ObservedObject,正确监听外部传入的ObservableObject:

struct ContentView: View {
    @ObservedObject var store: PresetStore
    
    var body: some View {
        ScrollView(.horizontal) {
            HStack {
                ForEach(store.arrayOfPresets) { bank in
                    PresetBankView(bank: bank)
                }
            }
            .padding()
        }
    }
}

3. 简化PresetBankView

删除冗余的@State变量,直接依赖bank的属性:

struct PresetBankView: View {
    let bank: PresetBank
    
    var body: some View {
        ZStack {
            VStack {
                Text(bank.title)
                    .multilineTextAlignment(.center)
                    .fontWeight(.medium)
                    .foregroundColor(bank.isSelected ? .white : .black)
                    .font(.system(size: 12))
                    .minimumScaleFactor(0.5)
                    .lineLimit(2)
                    .padding(.top, 10)
                    .padding(.horizontal, 5)
                
                Spacer()
                
                Text("\(bank.presetsCount)")
                    .multilineTextAlignment(.center)
                    .font(.system(size: 10))
                    .foregroundColor(bank.isSelected ? .white : .black)
                    .padding(.bottom, 10)
            }
            
            Image(systemName: bank.iconString)
                .font(.system(size: 26))
                .foregroundColor(bank.isSelected ? .white : .black)
                .padding(.top, 10)
        }
        .frame(width: 100, height: 100)
        .background {
            RoundedRectangle(cornerRadius: 8)
                .foregroundColor(bank.isSelected ? .black : .white)
                .shadow(color: .black.opacity(0.3), radius: 5)
                .overlay(
                    RoundedRectangle(cornerRadius: 8)
                        .stroke(.gray, lineWidth: 2)
                )
        }
        .padding(.trailing, 15)
    }
}

4. 优化ViewController的初始化逻辑

直接将预设数组传入PresetStore初始化,同时完善UIHostingController的生命周期:

class ViewController: UIViewController {
    var thisStore: PresetStore!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let p1 = PresetBank(id: UUID(), index: 0, title: "Default", iconString: "music.note.house", presetsCount: 5, isSelected: true)
        let p2 = PresetBank(id: UUID(), index: 1, title: "Relaxation", iconString: "figure.mind.and.body", presetsCount: 2)
        let p3 = PresetBank(id: UUID(), index: 2, title: "Dinner Party on Sunday Afternoon", iconString: "wineglass.fill", presetsCount: 3)
        let p4 = PresetBank(id: UUID(), index: 3, title: "Workout", iconString: "figure.run", presetsCount: 5)
        let p5 = PresetBank(id: UUID(), index: 4, title: "Sleepy Time", iconString: "powersleep", presetsCount: 2)
        
        thisStore = PresetStore(arrayOfPresets: [p1, p2, p3, p4, p5])
        
        let contentView = UIHostingController(rootView: ContentView(store: thisStore))
        addChild(contentView)
        view.addSubview(contentView.view)
        contentView.view.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: 200)
        contentView.didMove(toParent: self)
    }
    
    @IBAction func testSelect(_ sender: UIButton) {
        thisStore.selectBank(index: 1)
    }
}

关键知识点总结

  • 对于值类型(struct)的数组,必须通过替换整个数组的方式触发@Published的更新通知,单独修改元素属性不会触发视图刷新。
  • 监听外部传入的ObservableObject要使用@ObservedObject,内部创建的则使用@StateObject。
  • SwiftUI视图应尽量直接依赖数据源的属性,避免冗余的状态变量。

内容的提问来源于stack exchange,提问作者Eric Dolecki

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 14:48:26