已发布的属性未在 SwiftUI 中更新

Posted

技术标签:

【中文标题】已发布的属性未在 SwiftUI 中更新【英文标题】:Published property not updating in SwiftUI 【发布时间】:2021-12-31 22:14:45 【问题描述】:

我有以下代码,我希望 Published 属性标签的更新会反映在 UI 中,但事实并非如此。

  public class ViewSearch 
    @ObservedObject var viewModel: ViewModel
  
  /// Main initializer for instance.
  /// - Parameter viewModel: The  view model for searching.
  public init(viewModel: ViewModel) 
    self.viewModel = viewModel
  
  
  func doSomething()  
    for i in 1...1000000 
      if i % 250000  == 0 
        viewModel.label = "value: \(i)"
      
    
    viewModel.label = "Done!"
  


public class ViewModel: ObservableObject 
  @Published public var label = "initial value" 
    didSet 
      print("\(label)")
      self.objectWillChange.send()
    
  
  @Published public var searchText = ""
  
  var search: ViewSearch? = nil


struct ContentView: View 
  @ObservedObject var model: ViewModel

  var body: some View 
    TextField("Search", text: $model.searchText)  isEditing in
      if isEditing  
        model.label = "initial value"
      
     onCommit: 
      if !model.searchText.isEmpty 
        model.search = ViewSearch(viewModel: model)
        model.search?.doSomething()
      
    

    Text(model.label)
  

在提交文本字段输入时触发更新。我希望 UI 显示“值:250000”、“值:500000”等。 didSet 观察者显示更改但 UI 未更新。为什么不呢?

【问题讨论】:

【参考方案1】:

for 循环将阻塞主队列,因此创建一个队列以在另一个线程上运行 for i in 1...1000000

    //
    //  ContentView.swift
    //  ***
    //
    //  Created by Mustafa T Mohammed on 12/31/21.
    //

import SwiftUI

public class ViewSearch 
    @ObservedObject var viewModel: ViewModel

        /// Main initializer for instance.
        /// - Parameter viewModel: The  view model for searching.
    public init(viewModel: ViewModel) 
        self.viewModel = viewModel
    
    func doSomething()  
        let q = DispatchQueue.init(label: "doSomething")
            // for loop will block the main queue so create a queue to run for i in 1...1000000
            // on another thread
        q.async  [weak self] in // weak self to prevent retain cycle
            guard let self = self else  return 
            for i in 1...1000000 
                if i % 250000  == 0 
                    DispatchQueue.main.async  //update UI by coming back to main thread
                        self.viewModel.label = "value: \(i)"
                    
                
            
            DispatchQueue.main.async  //update UI by coming back to main thread
                self.viewModel.label = "Done!"
            
        
    


public class ViewModel: ObservableObject 
    @Published public var label = "initial value" 
        didSet 
            print("\(label)")
            self.objectWillChange.send()
        
    
    @Published public var searchText = ""

    var search: ViewSearch? = nil


struct ContentView: View 
    @ObservedObject var model: ViewModel

    var body: some View 
        TextField("Search", text: $model.searchText)  isEditing in
            if isEditing  
                model.label = "initial value"
            
         onCommit: 
            if !model.searchText.isEmpty 
                model.search = ViewSearch(viewModel: model)
                model.search?.doSomething()
            
        

        Text(model.label)
    

【讨论】:

很好......我已经尝试了 DispatchQueue.main,但如果 for 循环仍在主队列上运行,我想我对 DispatchQueue.main 的使用什么也没做。

以上是关于已发布的属性未在 SwiftUI 中更新的主要内容,如果未能解决你的问题,请参考以下文章

已发布值上的 SwiftUI toggle() 函数停止使用 Swift 5.2 触发 didSet

SwiftUI @Binding 未在子项中更新

API 已获取但未在 SwiftUI 中显示任何数据

SwiftUI:如何使用位置管理器类的已发布属性更新一个类(ObservableObject)的已发布属性?

SwiftUI 中已发布的计算属性未更新

不应发生的 Swift 内存冲突