代码之家  ›  专栏  ›  技术社区  ›  Ron Srebro

SwiftUI中的任务运行,但工作表内的视图未更新

  •  0
  • Ron Srebro  · 技术社区  · 2 年前

    我有一个简单的视图,它使用一个类生成一个链接供用户共享。

    此链接是异步生成的,因此使用.task修饰符运行。

    class SomeClass : ObservableObject {
    
    func getLinkURL() async -> URL {
        
        try? await Task.sleep(for: .seconds(1))
        return URL(string:"https://www.apple.com")!
      }
    }
    
    struct ContentView: View {
    
    @State var showSheet = false
    @State var link : URL?
    @StateObject var someClass = SomeClass()
    
    var body: some View {
        VStack {
            Button ("Show Sheet") {
                showSheet.toggle()
            }
        }
        .padding()
        .sheet(isPresented: $showSheet) {
            if let link = link {
                ShareLink(item: link)
            } else {
                HStack {
                    ProgressView()
                    Text("Generating Link")
                }
                
            }
        }.task {
            let link = await someClass.getLinkURL()
            print ("I got the link",link)
            await MainActor.run {
                self.link = link
            }
        }
        
       }
    }
    

    我已经将我的实际代码简化为这个仍然显示相同行为的示例。 当视图出现时,任务会正确执行,并且我看到了链接的调试打印。但是,当按下按钮显示图纸时,链接为零。

    enter image description here

    我找到的解决方法是将.task修饰符移动到工作表内,但这对我来说没有意义,我也不明白为什么这样做有效。

    这是一个bug,还是我遗漏了什么?

    0 回复  |  直到 2 年前
        1
  •  1
  •   malhal Benzy Neez    2 年前

    这是因为工作表的闭包是在显示之前创建的,并且它已经捕获了零链接的旧值。为了使其具有最新的值,即创建一个使用新值的新闭包,然后将其添加到捕获列表中,例如。

    .sheet(isPresented: $showSheet) { [link] in
    

    您可以在中了解有关此问题的更多信息 this answer 另一个问题。而且 someone 苹果公司要求提交错误报告的用户使用捕获列表。

    顺便说一句 .task 旨在消除对状态对象的需求,以便执行与视图生命周期相关的异步工作。此外,你不需要 MainActor.run