【问题标题】:How to make swiftui struct mutable如何使swiftui结构可变
【发布时间】:2021-01-31 09:31:03
【问题描述】:

我有以下结构:

struct Communication: Identifiable {
    let id: String
    let message, title: String
    let date: Date
    var isExpanded: Bool = false
}

问题是当我尝试更改isExpanded 的值时,我得到了错误:

不能对不可变值使用变异成员:'communication' 是一个'let' 常量

我正在尝试在SwiftUI 中的List() 中使用它

在 Swift 中,这类问题的正确解决方案是什么?

我使用 DatabaseService 类来获取结果:

class DatabaseService : ObservableObject {

@Published var communications = [Communication]()

funct getCommunications() -> Void {
    //...Add items to communications
}

然后在我看来

struct MainView: View {
    
    @ObservedObject var databaseService = DatabaseService()

    var body: some View {
        List(databaseService.communications) { communication in
                VStack(alignment: .leading) {
                    Text(communication.title)
                        .bold()
                        .font(.title3)
                    Text(communication.message)
                        .padding(.bottom, 24)
                        .lineLimit(communication.isExpanded ? nil : 5)
                        .overlay(
                            GeometryReader { proxy in
                                Button(action: {
                                    communication.isExpanded.toggle()
                                }) {
                                    Text(communication.isExpanded ? "Mai puțin" : "Mai mult")
                                        .font(.caption).bold()
                                        .padding(.leading, 8.0)
                                        .padding(.top, 8.0)
                                }
                                .frame(width: proxy.size.width, height: proxy.size.height, alignment: .bottomTrailing)
                            }
                        )
                }
            }
            .onAppear(perform: {
                self.databaseService.getComunicate()
            })
    }

【问题讨论】:

  • 我认为这是因为您正在创建一个 Communication 变量,即 let。在您的代码中搜索 let communication 并将其更改为 var communication。它与结构本身无关
  • @אוריorihpt 它是 swiftui 中的一个列表,该变量是列表中的迭代。
  • 这取决于您如何在 SwiftUI 视图中定义它。您会显示相关视图吗?
  • @Asperi 我已经改变了问题。

标签: swift swiftui


【解决方案1】:

是的,在ForEach 容器中,您使用的是数据元素的恒定副本,因此无法更改它(即使您愿意,无论如何它都是副本,所以没有意义)。相反,我们必须修改相应的元素 inside 视图模型集合,比如

@ObservedObject var ds = DatabaseService()    // << to shorten
var body: some View {
    List(ds.communications.indices, id: \.self) { i in
            VStack(alignment: .leading) {
                Text(ds.communications[i].title)
                    .bold()
                    .font(.title3)
                Text(ds.communications[i].message)
                    .padding(.bottom, 24)
                    .lineLimit(ds.communications[i].isExpanded ? nil : 5)
                    .overlay(
                        GeometryReader { proxy in
                            Button(action: {
                                ds.communications[i].isExpanded.toggle() // << now modified in VM !!
                            }) {
                                Text(ds.communications[i].isExpanded ? "Mai puțin" : "Mai mult")
                                    .font(.caption).bold()
                                    .padding(.leading, 8.0)
                                    .padding(.top, 8.0)
                            }
                            .frame(width: proxy.size.width, height: proxy.size.height, alignment: .bottomTrailing)
                        }
                    )
            }
        }

【讨论】:

  • 我们的对象不应该扩展一些东西以便我们可以使用 id: \.self 吗?
  • 我们通过索引而不是对象进行迭代。
【解决方案2】:

将选择逻辑提取到您的 DatabaseService 是最干净的:

class DatabaseService : ObservableObject {

    @Published private(set) var communications = [Communication]()

    public func didSelect(_ communication: Communication) -> Void {
    
        // find the index of the communication tapped by user
        guard let selectedIndex = communications.firstIndex(where: { $0.id == comunication.id }) else {
            return
        }
        // make a local copy so you can mutate it
        var communication = communication
        // toggle the selection
        comunication.selected.toggle()
        // assign the updated value to right index in the array, which will trigger the Publisher
        communications[selectedIndex] = comunication
    
    }

 }

然后在你的视图中你可以这样调用函数:

Button(action: { databaseService.didSelect(communication) })

而且您不需要更改任何其他内容。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-03
    • 2019-10-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多