json - 数据加载到视图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
解决方案
如果你遵循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")
}
)
推荐阅读
- python - 如何在请求中使用分页
- laravel-backpack - JSVALIDATOR 用于 LARAVEL 背包
- php - ACF 动态填充选择字段的选项 - 不完全工作
- angular - 表单以角度提交并通过POST将数据存储到mysql DB
- python - 蜘蛛没有回应
- python-3.x - 如何为使用 Python SDK 在 Azure 数据湖中创建的文件设置过期日期?
- excel - Excel vlookup 功能未按预期工作:标志创建
- java - 减少休眠中的数据量,同时仍然使用休眠功能
- xaml - 符号在按钮内被压扁
- python - sqlalchemy - 添加孩子时不调用 event.listens_for(Parent.child, "append")