首页 > 解决方案 > 数据加载到视图SwiftUI后执行函数

问题描述

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

标签: jsonswiftswiftui

解决方案


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

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

@ObservedObject var FetchResults = fetchResults()

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

@StateObject var fetchResults = FetchResults()

查看Paul Hudson的这篇文章,了解何时使用@StateObject和何时使用@ObservableObject.

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

您需要重用FetchResults已经加载其数据的,所以只需说self.snapshot()

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

推荐阅读