如何在视图中显示来自 Swift 完成处理程序的返回值?

Posted

技术标签:

【中文标题】如何在视图中显示来自 Swift 完成处理程序的返回值?【英文标题】:How to display in a View a returned value from a completion handler in Swift? 【发布时间】:2021-12-20 01:58:03 【问题描述】:

我有一个有这两种方法的类:

private func send(method: String, path: String, code: Array<Int>, headers: HTTPHeaders, completionHandler: @escaping (Int) -> Void) 
        let url: String = "\(self.credentials.url)/\(path)"

        AF.request(url, method: HTTPMethod(rawValue: method), headers: headers)
            .authenticate(with: self.request_credentials)
            .response  response in
                let status_code: Int = response.response!.statusCode
                
                completionHandler(status_code as Int)
            

func list_files(path: String) 
        let headers: HTTPHeaders = [
            "Depth": "1"
        ]
        
        send(method: "PROPFIND", path: path, code: [207, 301], headers: headers)  status_code in
            self.status_code = String(status_code)
        

因此这两个函数都使用完成处理程序,因为 Alamofire 在其发出 HTTP 请求的过程中使用它们。 我知道我必须以这种方式使用完成处理程序来处理这些异步数据。

我当前的问题是现在我必须在视图中显示这些数据(比如说self.status_code),但我不知道该怎么做。当我这样显示时:

struct ContentView: View 
    var body: some View 
        
        let auth = Authentication(username: "****",
                                  domain: "****",
                                  password: "****",
                                  port: ****,
                                  proto: "****",
                                  path:"****")
        
        let commands = Commands(credentials: auth.get_credentials())
        
        let _ = commands.list_files(path: "/")
        
        Text(commands.status_code)
            .padding()
    

它将显示status_code的初始化值(即0),而不是list_files方法的更新值。我知道这是因为这些完成处理程序的异步行为,并且当我显示它时该值尚未更新。

但问题是:我怎样才能设法将这个更新后的值正确地显示给用户?

我可能在这里做错了很多事情,我不介意收到完全不同的解决方案,因为我愿意遵循最佳做法。

谢谢。

【问题讨论】:

在你的方法 list_files 中传递一个 completionHandler 并在 UI 中显示这个完成的值。 这也是我尝试过的,但在视图中处理完成处理函数似乎很困难(或不可能?),你有什么例子可以说明我如何实现这一点吗?不过我会进一步检查。谢谢。 'viewModel.isRefreshing [weak self](loading) in if loading self?.updateUI() ' 【参考方案1】:

下面是一个带有完整示例的 Playground。我将介绍一些需要注意的重要事项。

首先我简化了“发送”方法,因为我没有原始示例中的所有类型和内容。此发送将等待 3 秒,然后使用您提供的任何消息调用完成处理程序。

在视图内部,当按钮被按下时,我们调用“发送”。然后,在完成处理程序中,您会注意到:

DispatchQueue.main.async 
    message = msg

我不知道发送中的Timer 将使用哪个线程来调用我的完成处理程序。但是 UI 更新需要在主线程上进行,因此 DispatchQueue.main... 构造将确保 UI 更新(将 message 设置为 msg)将在主线程上进行。

import UIKit
import SwiftUI
import PlaygroundSupport

func send(message: String, completionHandler: @escaping (String) -> Void) 
    Timer.scheduledTimer(withTimeInterval: 3.0, repeats: false)  timer in
        completionHandler(message)
        timer.invalidate()
    


struct ContentView: View 
    @State var message : String = ""

    var body: some View 
        print("Building view")
        return VStack 
            TextField("blah", text: $message)
            Button("Push Me", action: 
                send(message: "Message Received")  msg in
                    DispatchQueue.main.async 
                        message = msg
                    
                
            )
        .frame(width: 320, height: 480, alignment: .center)
    



let myView = ContentView()
let host = UIHostingController(rootView: myView)
PlaygroundSupport.PlaygroundPage.current.liveView = host

【讨论】:

谢谢。我将您的示例修改为我的代码:在我的类中声明 @State var status_code: String 并在此类的 list_files 方法中使用 DispatchQueue.main.async 包装 self.status_code = String(status_code)(如我的原始消息所示)。我没有更改视图正文中的任何内容。 status_code 仍然显示“0”,我做错了什么? 当我将我的类初始化为:@ObservedObject var commands: Commands 并且我想在类内跟踪的值为:@Published var status_code: String 时,它可以工作。在这种情况下,一旦 status_code 值发生变化,视图就会自动更新(我可以看到它从 0 变为 207)。对于这种特定的课程案例,我的方法是否正确?它有效,但我不确定这是否是正确的做法,或者是否有更好的方法。谢谢。

以上是关于如何在视图中显示来自 Swift 完成处理程序的返回值?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Swift 完成处理程序的结果?

Swift 使用完成处理程序将视图锁定在一个控制器中以反映在另一个控制器中

如何在swift中打开另一个动画完成视图?

在 Swift 中创建可重用视图并添加完成处理程序

使用 Swift for iOS 处理来自关闭应用程序的推送通知,如何显示它然后返回关闭或暂停?

Swift 3.0:使用 URLSessionDownloadDelegate 完成下载后如何在不同的视图控制器中打开 PDF