数据加载到视图SwiftUI后执行功能

Posted

技术标签:

【中文标题】数据加载到视图SwiftUI后执行功能【英文标题】:Executing function after data loads into view SwiftUI 【发布时间】:2021-09-16 13:45:23 【问题描述】:

我希望在我的 ContentView 加载所有网络数据后执行快照功能。我对 API 调用没有任何问题,我只是希望 Snapshot 函数在所有数据加载到 ContentView 后运行。

按下按钮后的预期结果: Expected result after pressing button

按下按钮后的实际结果: Actual result after pressing button

ContentView.swift


extension View 
    
    func Snapshot() -> UIImage 
        let controller = UIHostingController(rootView: self)
        let view = controller.view
        
        let targetSize = controller.view.intrinsicContentSize
        view?.bounds = CGRect(origin: .zero, size: targetSize)
        view?.backgroundColor = .clear
        
        let renderer = UIGraphicsImageRenderer(size: targetSize)
        
        return renderer.image  _ in
            view?.drawHierarchy(in: controller.view.bounds, afterScreenUpdates: true)
        
    
    




struct ContentView: View 
    
    @ObservedObject var FetchResults = fetchResults()
    
    var contentView: some View 
        
        ContentView()
        
    
    
    var body: some View 
        
        Group 
            if FetchResults.dataHasLoaded 
                
                VStack 
                    Text(FetchResults.clout.postFound?.body ?? "n/a")
                        .padding()
                        .background(Color.blue)
                        .mask(RoundedRectangle(cornerRadius: 30, style: .continuous))
                        .shadow(color: Color(#colorLiteral(red: 0, green: 0, blue: 0, alpha: 1)).opacity(0.3), radius: 10, x: 0, y:10)
                        .padding()
                        .frame(maxWidth: 300)
                    
                    
                
                
                Button(action: 
                    
                    let image = contentView.Snapshot()
                    print(image)
                    
                , label:Text("press me to print view"))
                
                
             else 
                
                Text("loading data")
                
            
        
        
        
        
    
 

GotPost.swift -- 我的 ViewModel 运行 API 调用


class fetchResults: ObservableObject 
    
    @Published var clout = Cloutington()
    @Published var dataHasLoaded = false
    
    
    init() 
        
        getData  clout in
            self.clout = clout
        
        
    
    
    private func getData(completion: @escaping (Cloutington) -> ()) 
        
        let parameters =  "\r\n \"PostHashHex\": \"2f9633c2460f23d9d5690fe9fd058457ebeb01f6f75805aa426cdaab90a1f4b4\"\r\n"
        let postData = parameters.data(using: .utf8)
        var request =  URLRequest(url: URL(string: "https://bitclout.com/api/v0/get-single-post")!,timeoutInterval: Double.infinity)
        request.addValue("application/json", forHTTPHeaderField: "Content-Type")
        request.httpBody = postData
        request.httpMethod = "POST"
        
        let task =  URLSession.shared.dataTask(with: request)  (responseData, response, error) in

            print(error)
            print(response)
            print(responseData)
            
            if let resData = responseData 
                let decoder = JSONDecoder()
                
                do
                
                    let finalData = try decoder.decode(Cloutington.self, from: resData)
                    DispatchQueue.main.async 

                        completion(finalData)
                        self.dataHasLoaded = true

                    
                    
                
                catch (let error)
                
                    print(error)
                

            
            
        
        task.resume()
        
    
    

PostFound.swift -- 模型解析 JSON 数据


struct Cloutington: Decodable 
    
    var postFound: PostFound?
    
    enum CodingKeys: String, CodingKey 
        case postFound = "PostFound"
        
    
    


struct PostFound: Decodable 
    
    var id: String?
    var postHashHex, posterPublicKeyBase58Check, parentStakeID, body: String?
    var imageURLs: [String]?
    //    var recloutedPostEntryResponse: JSONNull?
    var creatorBasisPoints, stakeMultipleBasisPoints: Int?
    var timestampNanos: Double?
    var isHidden: Bool?
    var confirmationBlockHeight: Int?
    var inMempool: Bool?
    var profileEntryResponse: ProfileEntryResponse?
    var likeCount, diamondCount: Int?
    var isPinned: Bool?
    var commentCount, recloutCount: Int?
    var diamondsFromSender: Int?
    
    enum CodingKeys: String, CodingKey 
        case postHashHex = "PostHashHex"
        case posterPublicKeyBase58Check = "PosterPublicKeyBase58Check"
        case parentStakeID = "ParentStakeID"
        case body = "Body"
        case imageURLs = "ImageURLs"
        case creatorBasisPoints = "CreatorBasisPoints"
        case stakeMultipleBasisPoints = "StakeMultipleBasisPoints"
        case timestampNanos = "TimestampNanos"
        case isHidden = "IsHidden"
        case confirmationBlockHeight = "ConfirmationBlockHeight"
        case inMempool = "InMempool"
        case profileEntryResponse = "ProfileEntryResponse"
        case likeCount = "LikeCount"
        case diamondCount = "DiamondCount"
        case isPinned = "IsPinned"
        case commentCount = "CommentCount"
        case recloutCount = "RecloutCount"
        case diamondsFromSender = "DiamondsFromSender"
    


// MARK: - ProfileEntryResponse
struct ProfileEntryResponse: Decodable 
    var publicKeyBase58Check, username, profileEntryResponseDescription, profilePic: String?
    var isHidden, isReserved, isVerified: Bool?
    var coinPriceBitCloutNanos, stakeMultipleBasisPoints: Int?
    
    enum CodingKeys: String, CodingKey 
        case publicKeyBase58Check = "PublicKeyBase58Check"
        case username = "Username"
        case profileEntryResponseDescription = "Description"
        case profilePic = "ProfilePic"
        case isHidden = "IsHidden"
        case isReserved = "IsReserved"
        case isVerified = "IsVerified"
        case coinPriceBitCloutNanos = "CoinPriceBitCloutNanos"
        case stakeMultipleBasisPoints = "StakeMultipleBasisPoints"
    
 ```

Really appreciate the help ????

【问题讨论】:

这是一个计算属性,因此您可能会重新创建内容视图而不是实际拍摄屏幕快照。试试self.Snapshot() @DeveloperDank 如果你使用 self.Snapshot(),它也会在快照中包含“按我打印视图”。 【参考方案1】:

如果您遵循Swift API Design Guidelines 来命名类型和变量,您通常会获得更多更好的帮助。这意味着对属性和方法的首字母使用小写,对类型的首字母使用大写。我了解您可能来自 C# 等约定不同的平台。但是当你不以 Swift 的方式做事时(或者更糟糕的是,在你的代码中,你两者都做!),你会让我们更难理解你的代码。

另外,这是一个糟糕的模式:

@ObservedObject var FetchResults = fetchResults()

问题在于 SwiftUI 不理解 FetchResults 对象的所有权,并且每次重新创建视图时都会重新创建它。在这种情况下,您应该改用@StateObject

@StateObject var fetchResults = FetchResults()

查看this article by Paul Hudson,了解何时使用@StateObject 以及何时使用@ObservableObject

但是,这并不能解决您发布的问题。您的代码通过说contentView.Snapshot() 创建快照,它调用您的contentView 计算属性,它从头开始创建一个新的ContentView,它创建一个新的、未加载的FetchResults(无论您使用的是@ObservedObject 还是@ 987654334@).

你需要重用已经加载了数据的FetchResults,所以直接说self.snapshot()吧:

Button(
    action: 
        let image = self.snapshot()
        print(image)
    ,
    label: 
        Text("press me to print view")
    
)

【讨论】:

抢你的传奇!谢谢你,非常感谢你的建议?

以上是关于数据加载到视图SwiftUI后执行功能的主要内容,如果未能解决你的问题,请参考以下文章

为啥核心数据不会在 swiftui 类中加载,但会在结构视图中加载

LinkPresentation 视图未在 SwiftUI 中完全加载

SwiftUI 在哪里加载 MVVM 中的数据

合并SwiftUI远程获取数据-ObjectBinding不会更新视图

一旦新的 SwiftUI 视图完全加载,@State 变量就会被清除

如何在 SwiftUI 中启动应用程序时有条件地加载视图?