SwiftUI错误:不允许从视图更新中发布更改,这将导致未定义的行为

vlju58qv  于 2023-05-05  发布在  Swift
关注(0)|答案(1)|浏览(117)

你好,我正试图创建一个消息应用程序,我想添加一个日期或天为每组消息的标题。当我尝试在viewModel中追加数组时,我得到错误“不允许在视图更新中发布更改,这将导致未定义的行为。”我尝试将其 Package 在Dispatch.main.async中,但不起作用,有人知道修复方法吗,谢谢。
观点

import SwiftUI
import Firebase
struct MessagesView: View {
    let messages: [Message]
    @EnvironmentObject var viewModel: MessageViewModel
    var body: some View {
        VStack{
            ScrollView{
               LazyVStack(spacing: 3){ 
                   ForEach(messages){ message in
                       if let dateStr = viewModel.getDate(date: message.timestamp) {
                             Text(dateStr)
                       }
                       MessageBubble(message: message)
                   }                               
                }  
            }
        }
    }
}

viewModel

import Foundation
class MessageViewModel: Identifiable, ObservableObject {
    @Published var dayArr = [String]()  

    func getDate(date: Timestamp) -> String? {
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "MM/dd/yyyy"

        let val = date.dateValue()
        let dateString = dateFormatter.string(from: val)

        let calendar = Calendar.current
        let today = calendar.startOfDay(for: Date())
        let yesterday = calendar.date(byAdding: .day, value: -1, to: today)!
        let dateToCompare = calendar.startOfDay(for: val)

        if dateToCompare == today {
            if !dayArr.contains("Today"){
                dayArr.append("Today") //error here when trying to append
                return "Today"
            } else{
                return nil
            }
        } else if dateToCompare == yesterday {
            if !dayArr.contains("Yesterday"){
                dayArr.append("Yesterday") //error here when trying to append
                return "Yesterday"
            } else {
                return nil
            }
        } else {
            if !dayArr.contains(dateString){
                dayArr.append(dateString) //error here when trying to append
                return dateString 
            } else{
                return nil
            }
        }
    }      
}
beq87vna

beq87vna1#

因为每个Message都有自己的getDate,所以方法应该在Message内部。

struct Message: Identifiable{
    let id: UUID = .init()
    let timestamp: Date = Calendar.current.date(byAdding: .day, value: (-10...10).randomElement()!, to: Date())! //Using Date instead of Timestamp, you can change the type
    var getDate : String {
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "MM/dd/yyyy"

        let val = self.timestamp//.dateValue() //Adjust the type back to Timestamp
        let dateString = dateFormatter.string(from: val)

        let calendar = Calendar.current
        let today = calendar.startOfDay(for: Date())
        let yesterday = calendar.date(byAdding: .day, value: -1, to: today)!
        let dateToCompare = calendar.startOfDay(for: val)

        if dateToCompare == today {
            return "Today"
        } else if dateToCompare == yesterday {
            return "Yesterday"
        } else {
            return dateString

        }
    }
}

然后在View中,您可以使用mapSet将这些值组合在一起并创建一个唯一的序列。

import SwiftUI
struct MessagesView: View {
    let messages: [Message] = (1...50).map { _ in
            .init()
    }//Sample data
    @StateObject var viewModel: MessageViewModel = .init() //Can leave as EnvironmentObject in your project, change to StateObject for a sample
    
    var dayArr : Set<String> { //Set automatically removes duplicates
        Set(messages.map { m in
            m.getDate
        })
    }

    var body: some View {
        VStack{
            ScrollView{
               LazyVStack(spacing: 3){
                   ForEach(messages){ message in
                       Text(message.getDate)
                       
                       //MessageBubble(message: message)
                   }
                }
            }
        }.onAppear(){
            viewModel.dayArr = Array(dayArr) //Only if you need this
        }
    }
}

使用此方法,您还可以使用字典对消息进行分组。

var dayGroups: [String: [Message]]{ //Group by getDate
        Dictionary(grouping: messages, by: \.getDate)
    }

然后你就可以让swift完成所有的工作,只使用它来显示,而不需要任何其他条件。

import SwiftUI
struct MessagesView: View {
    let messages: [Message] = (1...50).map { _ in
            .init()
    }//Sample data
    @StateObject var viewModel: MessageViewModel = .init() //Can leave as EnvironmentObject in your project, change to StateObject for a sample
    
    var dayGroups: [String: [Message]]{ //Group by getDate
        Dictionary(grouping: messages, by: \.getDate)
    }
    var body: some View {
        VStack{
            ScrollView{
                ForEach(Array(dayGroups), id:\.key){ (date, messages) in
                    DisclosureGroup {
                        LazyVStack(spacing: 3){
                            ForEach(messages){ message in
                                Text(message.getDate)
                                
                                //MessageBubble(message: message)
                            }
                        }
                    } label: {
                        Text("\(date)\t\t\(messages.count.description)")
                    }
                }
            }
        }
    }
}

相关问题