如何使swiftui结构可变

Posted

技术标签:

【中文标题】如何使swiftui结构可变【英文标题】:How to make swiftui struct mutable 【发布时间】: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 我已经改变了问题。 【参考方案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) )

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

【讨论】:

以上是关于如何使swiftui结构可变的主要内容,如果未能解决你的问题,请参考以下文章

如何构造值以使所有视图都可以在 Swift/SwiftUI 中访问其值?

如何在 SwiftUI 文本元素中创建可变字符串?

Swiftui 无法分配给属性:'self' 是不可变的错误

在 SwiftUI 中将视图添加到层​​次结构时如何动画过渡

如何使用 SwiftUI 使这个 ForEach 函数工作?

SwiftUI:如何使列表视图透明?如何更改列表视图背景颜色?