UIKit集成SwiftUI:点击UIKit按钮无法更新SwiftUI视图选中状态
问题解决:UIKit嵌入SwiftUI后无法更新选中状态
问题根源
你遇到的视图不更新问题,核心原因有三个:
- 值类型数组的更新机制限制:
PresetBank是struct值类型,修改数组内元素的属性时,数组本身的引用未发生变化,@Published只会在数组被整体替换时才会发送视图更新通知。 - ObservableObject的监听方式错误:
ContentView中用@State存储PresetStore(ObservableObject),无法正确监听数据变化。 - 冗余状态变量:
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
相关产品推荐
相关产品推荐

