【问题标题】:In a List. Left side of mutating operator isn't mutable: 'item' is a 'let' constant在一个列表中。变异运算符的左侧不可变:'item' 是 'let' 常量
【发布时间】:2021-12-02 08:15:28
【问题描述】:

Xcode 12. 代码和生命周期都是 Swiftui。

我查看了其他人的相关问题,似乎他们只是将整个项目代码转储在这里;从中挑选我需要的东西有点不知所措。 因此,我已将问题分解为最简单的示例。

目标是让 numberX 在我按下 + 按钮时进行迭代。

提前致谢!

struct InfoData: Identifiable {
    var id = UUID()
    var nameX: String
    var numberX: Int
}

class ContentX: ObservableObject {
    @Published var infoX = [
        InfoData(nameX: "Example", numberX: 1)
    ]
}

struct ContentView: View {
    @StateObject var contentX = ContentX()
    var body: some View {
        List(contentX.infoX) { item in
            HStack {
                Text("\(item.nameX)")
                Spacer()
                Button("+") {
                    item.numberX += 1 //Eror shows up here <<
                }
                Text("\(item.numberX)")
            }
        }
    }
}

【问题讨论】:

  • 调用 contentX 并让它直接用所需的值更新 infoX 数组。

标签: swift xcode list swiftui mutable


【解决方案1】:

在您使用的语法中,item 是一个不可变值,正如错误所告诉您的那样。您不能对其进行变异,因为它并不代表与它所来自的数组的真正连接——它只是在List 迭代中使用的临时可读副本。

如果您可以升级到 Xcode 13,则可以访问 ListForEach 中称为元素绑定语法的东西,让您这样做:

struct ContentView: View {
    @StateObject var contentX = ContentX()
    var body: some View {
        List($contentX.infoX) { $item in //<-- Here
            HStack {
                Text("\(item.nameX)")
                Spacer()
                Button("+") {
                    item.numberX += 1
                }
                Text("\(item.numberX)")
            }
        }
    }
}

这为您提供了一个可变的 Bindingitem,允许您更改其值并将它们反映在原始数组中。

在 Xcode 13/Swift 5.5 之前,您必须定义自己的方式来更改数组中的元素。这是一种解决方案:

class ContentX: ObservableObject {
    @Published var infoX = [
        InfoData(nameX: "Example", numberX: 1)
    ]
    
    func alterItem(item: InfoData) {
        self.infoX = self.infoX.map { $0.id == item.id ? item : $0 }
    }
}

struct ContentView: View {
    @StateObject var contentX = ContentX()
    var body: some View {
        List(contentX.infoX) { item in
            HStack {
                Text("\(item.nameX)")
                Spacer()
                Button("+") {
                    var newItem = item
                    newItem.numberX += 1
                    contentX.alterItem(item: newItem)
                }
                Text("\(item.numberX)")
            }
        }
    }
}

或者,使用自定义Binding 的另一个选项:


class ContentX: ObservableObject {
    @Published var infoX = [
        InfoData(nameX: "Example", numberX: 1)
    ]
    
    func bindingForItem(item: InfoData) -> Binding<InfoData> {
        .init {
            self.infoX.first { $0.id == item.id }!
        } set: { newValue in
            self.infoX = self.infoX.map { $0.id == item.id ? newValue : $0 }
        }
    }
}

struct ContentView: View {
    @StateObject var contentX = ContentX()
    var body: some View {
        List(contentX.infoX) { item in
            HStack {
                Text("\(item.nameX)")
                Spacer()
                Button("+") {
                    contentX.bindingForItem(item: item).wrappedValue.numberX += 1
                }
                Text("\(item.numberX)")
            }
        }
    }
}

【讨论】:

  • 非常感谢!我是编码新手,所以有时我还没有了解事情是如何工作的。我期待 Xcode 13,但我现在正处于课程的中间,我担心如果我更新我将无法跟进。不过,也许我的担心是没有根据的。
  • 是的,在你学习的时候保持舒适是个不错的主意。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-12-13
  • 1970-01-01
  • 2016-08-20
  • 2021-08-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多