You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI修改ObservableObject的@Published属性后视图未更新问题

问题根因

代码存在5个核心错误,直接导致@Published变量无法正常更新、分支逻辑不生效:

  • 类声明时ObservableObject拼写错误(写为ObesrvableObject),没有正确遵守可观察对象协议,@Published属性变更不会触发视图刷新
  • 在getBotResponse函数内部新建了完全独立的ChatBotVariables临时实例,所有属性修改都针对这个用完即销毁的临时对象,和视图里持有的状态实例没有任何关联
  • Switch判断时写了cbl.$answerCase,带$拿到的是属性包装器的绑定对象(Binding<String>类型),不是实际的字符串值,永远匹配不到字符串分支
  • 两个if判断条件完全重复,都判断tempMessage.contains("textA"),第二个分支永远不会执行
  • 在视图body闭包内直接写cbl.showFurtherContent.toggle()、print()这类副作用代码,SwiftUI的body闭包仅用于描述视图结构,直接写状态修改逻辑会触发不可控的无限重渲染
修正方案
  1. 修正ChatBotVariables的协议拼写错误
  2. 移除函数内部新建的临时状态实例,将视图持有的状态对象作为参数传入响应函数,所有修改都针对同一个状态源
  3. Switch判断时去掉$,直接读取cbl.answerCase的实际字符串值
  4. 修正重复的分支判断条件,替换为实际需要匹配的关键字
  5. 将状态修改、打印这类副作用逻辑移到onAppear等专门的生命周期回调中,不要直接写在body构建闭包里
  6. 严格遵循SwiftUI状态规则:@StateObject标记的实例是当前视图的唯一可信状态源,全局所有状态修改都要针对这一个实例,不要重复创建同类实例
修正后代码
// 修正协议拼写
class ChatBotVariables: ObservableObject{
    @Published var answerCase: String = "x"
    @Published var showFurtherContent: Bool = false
}

// 传入视图持有的状态实例,不要在函数内部新建状态
func getBotResponse(message: String, stateVM: ChatBotVariables) -> String {
   let tempMessage = message.lowercased()

   if tempMessage.contains("textA"){
     stateVM.answerCase = "text"
     stateVM.showFurtherContent = false
     return "some text for BotAnswer"
   // 替换为实际需要匹配的video分支关键字
   }else if tempMessage.contains("video"){
     stateVM.answerCase = "video"
     stateVM.showFurtherContent = true
     return "some other text for BotAnswer"
   }else{
     stateVM.answerCase = "questionbox"
     stateVM.showFurtherContent = true
     return "something else"
   }
}

struct ChatBot: View {
    //happyBar
    @State var _ratio = 60.0;
    @State var _start : Bool = false;

    @State private var messageText = ""
    @State var messages: [String] = [""]
    @State public var messageProperty = "Y"

    @State private var botWriting = false
    @State private var textInputEnabled = false

    @ObservedObject var vm = MainMessagesViewModel()
    // 视图唯一状态源
    @StateObject var cbl = ChatBotVariables()

    init() {
        initChatbot()
    }

    var body: some View {
       NavigationView {
           VStack {
               ScrollView{
                   ForEach(messages, id: \.self){ message in
                       if message.contains("[USER]"){
                           let newMessage = message.replacingOccurrences(of:"[USER]", with: "")
                           
                           HStack{
                               Spacer()
                               Text(newMessage)
                                   .padding()
                                   .foregroundColor(Color.white)
                                   .background(.blue.opacity(0.8))
                                   .cornerRadius(10)
                                   .padding(.horizontal, 16)
                                   .padding(.bottom, 10)
                           }
                       }else{
                           HStack{
                               Text(message)
                                   .padding()
                                   .background(.gray.opacity(0.15))
                                   .cornerRadius(10)
                                   .padding(.horizontal, 16)
                                   .padding(.bottom, 10)
                               
                               Spacer()
                           }
                       }
                   }.rotationEffect(.degrees(180))
               
                   // 直接读取answerCase字符串值匹配分支,移除直接写在body里的副作用代码
                   if cbl.showFurtherContent {
                       Group {
                           switch cbl.answerCase{
                               case "emoji":
                                   // 此处替换为emoji模块视图
                                   Text("emoji分支内容")
                               case "video":
                                   // 此处替换为视频模块视图
                                   Text("video分支内容")
                               case "yesno":
                                   // 此处替换为是/否按钮模块视图
                                   Text("yesno分支内容")
                               default:
                                   EmptyView()
                           }
                       }
                       .onAppear {
                           // 如果需要展示后自动隐藏,逻辑放在生命周期回调中
                           // DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
                           //     cbl.showFurtherContent = false
                           // }
                       }
                   }
                
               }.rotationEffect(.degrees(180))
               .background(Color.gray.opacity(0.10))
   
               if messages.count > 1 {
                   let mCount = messages.count
                   Text("count: \(mCount) answer: \(cbl.answerCase) Property: \(messageProperty)")
               }
           
               HStack {
                   if botWriting{
                       Image(systemName: "ellipsis.bubble.fill")
                           .font(.system(size: 26))
                           .foregroundColor(Color.blue)
                   }
               }
           
               Spacer()
          
               // 文本输入栏逻辑
               if textInputEnabled{
                   VStack{
                       HStack{
                           TextField("Text eingeben", text: $messageText)
                               .padding()
                               .background(Color.gray.opacity(0.1))
                               .cornerRadius(10)
                               .onSubmit{
                                   sendMessage(message: messageText)
                               }
                               .padding(EdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 0))
                       
                           Button{
                               sendMessage(message: messageText)
                           }label:{
                               Image(systemName: "paperplane.fill")
                           }
                           .font(.system(size: 26))
                           .padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 20))
                       }
                   
                       HStack{
                           Button{
                               // 选项按钮逻辑
                           }label:{
                               Image(systemName: "questionmark.circle")
                           }
                           .font(.system(size: 26))
                           .padding(EdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 0))

                           Spacer()
                       
                           Button{
                               textInputEnabled.toggle()
                           }label:{
                               Image(systemName: "keyboard.chevron.compact.down")
                           }
                           .font(.system(size: 26))
                           .padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 50))
                       
                           Spacer()
                       }
                   }
               
               }else{
                   HStack{
                       Button{
                           textInputEnabled.toggle()
                       }label:{
                           Image(systemName: "keyboard")
                       }
                       .font(.system(size: 26))
                   }
               }
           }
           .navigationBarHidden(true)
       }
    }

    func initChatbot(){
        messages.append("Hallo \(vm.chatUser?.email ?? ""), wie geht es dir heute INIT?")
    }

    func sendMessage(message: String){
        withAnimation{
            messages.append("[USER]" + message)
            self.messageText = ""
            botWriting.toggle()
        }

        DispatchQueue.main.asyncAfter(deadline: .now() + 1){
            withAnimation{
                // 调用响应函数时传入当前持有的状态实例
                messages.append(getBotResponse(message: message, stateVM: cbl))
                botWriting.toggle()
            }
        }
    }
}

内容的提问来源于stack exchange,提问作者IDK

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 03:39:23