Swift中无法修改Struct内Bool变量的问题解惑
问题原因
- Swift 中的
Struct是值类型,当你在List里遍历newspaperRegion的元素时,拿到的newspaper是一个不可变的let常量副本。值类型的副本默认无法修改,因此调用需要改变自身状态的mutating方法时就会触发报错。 - 即便用全局变量存储数据,遍历得到的依然是独立的副本,直接修改副本不会影响原全局变量,且副本本身是
let常量,无法调用mutating方法。
解决方案
方案1:通过索引定位原数据修改
利用遍历的索引直接修改全局变量里的原元素,绕过副本不可变的限制:
struct SettingsView: View { var body: some View { List { // 遍历地区并拿到索引 ForEach(Array(newspaperRegion.enumerated()), id: \.element.id) { regionIndex, region in Section(header: Text(region.name)) { // 遍历该地区的报纸并拿到索引 ForEach(Array(region.newspapers.enumerated()), id: \.element.id) { paperIndex, newspaper in HStack { Text(newspaper.name) Spacer() Button(action: { // 通过索引直接修改全局变量中的原数据 newspaperRegion[regionIndex].newspapers[paperIndex].changeFavorite() }) { Image(systemName: newspaper.favorite ? "star.fill" : "star") } } } } } } } }
方案2:将结构体改为引用类型(Class)
把 Newspaper 和 NewspaperRegion 改为 Class(引用类型),此时遍历拿到的是对象的引用,即使引用本身是 let 常量,也可以修改对象内部的属性:
class Newspaper: Identifiable { let id = UUID() var name: String var favorite: Bool init(name: String, favorite: Bool) { self.name = name self.favorite = favorite } // 无需mutating修饰,类方法可直接修改属性 func changeFavorite() { favorite.toggle() } } class NewspaperRegion: Identifiable { let id = UUID() var name: String var newspapers: [Newspaper] init(name: String, newspapers: [Newspaper]) { self.name = name self.newspapers = newspapers } } // 全局变量 var newspaperRegion: [NewspaperRegion] = [ NewspaperRegion(name: "北京", newspapers: [Newspaper(name: "北京日报", favorite: false)]) ] struct SettingsView: View { var body: some View { List { ForEach(newspaperRegion) { region in Section(header: Text(region.name)) { ForEach(region.newspapers) { newspaper in HStack { Text(newspaper.name) Spacer() Button(action: { newspaper.changeFavorite() }) { Image(systemName: newspaper.favorite ? "star.fill" : "star") } } } } } } } }
方案3:用SwiftUI状态管理(推荐)
全局变量不是SwiftUI的最佳实践,推荐用 @StateObject + @Published 管理数据,确保数据变化时视图自动刷新:
// 保持原结构体定义不变 struct Newspaper: Identifiable { let id = UUID() var name: String var favorite: Bool mutating func changeFavorite() { favorite.toggle() } } struct NewspaperRegion: Identifiable { let id = UUID() var name: String var newspapers: [Newspaper] } // 用ObservableObject封装数据 class NewspaperData: ObservableObject { @Published var regions: [NewspaperRegion] = [ NewspaperRegion(name: "北京", newspapers: [Newspaper(name: "北京日报", favorite: false)]) ] } struct SettingsView: View { @StateObject private var newspaperData = NewspaperData() var body: some View { List { // 用$绑定可变的地区对象 ForEach($newspaperData.regions) { $region in Section(header: Text(region.name)) { // 用$绑定可变的报纸对象 ForEach($region.newspapers) { $newspaper in HStack { Text(newspaper.name) Spacer() Button(action: { newspaper.changeFavorite() }) { Image(systemName: newspaper.favorite ? "star.fill" : "star") } } } } } } } }
这里的 $ 符号代表可变绑定,让 newspaper 变为可修改状态,同时 @Published 会自动触发视图刷新。
内容的提问来源于stack exchange,提问作者Despina
相关产品推荐
相关产品推荐

