我正在尝试使用SwiftUI的ObservableObject
,但似乎无法更新视图的属性。
这是我的ContentView.swift
import SwiftUI
final class JiraData: ObservableObject {
init() {
// TODO use real service
self.worklog = self.jiraService.getCounter(tickets: 2, minutes: 120, status: "BELOW")
}
init(hours: Double, tickets: Int, status: Status) {
self.worklog = Worklog(minutesLogged: Int(hours) * 60, totalTickets: tickets, status: status)
}
/// Refreshes the data in this object, by calling the underlying service again
///
/// Despite SwiftUI's Observable pattern, I need the UI to toggle this interaction
func refresh() {
self.worklog = self.jiraService.getCounter(tickets: Int.random(in: 3..<5), minutes: 390, status: "OK")
print("Now mocked is: \(self.worklog)")
}
func getTimeAndTickets() -> String {
return "You logged \(String(format: "%.2f", Double(self.worklog.minutesLogged) / 60.0)) hours in \(self.worklog.totalTickets) tickets today"
}
func getStatus() -> String {
// TODO removed hardcoded part
return "You are on \"\(Status.below.rawValue)\" status"
}
var jiraService = MockedService()
@Published var worklog: Worklog
}
struct ContentView: View {
@ObservedObject var jiraData = JiraData()
var body: some View {
VStack(alignment: .leading, spacing: 0) {
Text(jiraData.getTimeAndTickets())
.font(Font.system(size: 20.0))
.fontWeight(.semibold)
.multilineTextAlignment(.leading)
.padding(.horizontal, 16.0)
.frame(width: 360.0, height: 80.0, alignment: .topLeading)
Text(jiraData.getStatus())
.font(Font.system(size: 20.0))
.fontWeight(.semibold)
.multilineTextAlignment(.leading)
.padding(.horizontal, 16.0)
.frame(width: 360.0, height: 80, alignment: .topLeading)
Button(action: {
jiraData.refresh() // TODO unneeded?
})
{
Text("Refresh")
.font(.caption)
.fontWeight(.semibold)
}
Button(action: {
NSApplication.shared.terminate(self)
})
{
Text("Quit")
.font(.caption)
.fontWeight(.semibold)
}
.padding(.trailing, 16.0)
.frame(width: 360.0, alignment: .trailing)
}
.padding(0)
.frame(width: 360.0, height: 360.0, alignment: .top)
}
}
#if DEBUG
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(jiraData: JiraData(hours: 6.5, tickets: 2, status: Status.below))
}
}
#endif
数据结构为:
import Foundation
/// This is a Worklog possible status
enum Status: String, Codable, Equatable {
case below = "below"
case ok = "ok"
case overtime = "overtime"
public init(from decoder: Decoder) throws {
// If decoding fails, we default to "below"
guard let rawValue = try? decoder.singleValueContainer().decode(String.self) else {
self = .below
return
}
self = Status(rawValue: rawValue) ?? .below
}
}
/// This represents a Worklog on JIRA
struct Worklog: Codable {
var minutesLogged: Int
var totalTickets: Int
var status: Status
}
/// Parses a JSON obtained from the JAR's stdout
func parse(json: String) -> Worklog {
let decoder = JSONDecoder()
let data = Data(json.utf8)
if let jsonWorklogs = try? decoder.decode(Worklog.self, from: data) {
return jsonWorklogs
}
// TODO maybe handle error differently
return Worklog(minutesLogged: 0, totalTickets: 0, status: Status.below)
}
“我的服务”只是一个模拟:
import Foundation
struct MockedService {
func getCounter(tickets: Int, minutes: Int, status: String) -> Worklog {
let json = "{\"totalTickets\":\(tickets),\"minutesLogged\":\(minutes),\"status\":\"\(status)\"}"
print("At MockedService: \(json)")
return parse(json: json)
}
}
启动时,我将其打印在控制台上
At MockedService: {"totalTickets":2,"minutesLogged":120,"status":"BELOW"}
2020-11-09 20:47:17.163710-0300 JiraWorkflows[2171:14431] Metal API Validation Enabled
这时,我的应用看起来像这样(正确)。
我知道,到目前为止,UI看起来很糟糕:(
但是,尽管我在控制台上看到了Refresh
,但在单击2020-11-09 20:47:17.163710-0300 JiraWorkflows[2171:14431] Metal API Validation Enabled
At MockedService: {"totalTickets":4,"minutesLogged":390,"status":"OK"}
Now mocked is: Worklog(minutesLogged: 390, totalTickets: 4, status: JiraWorkflows.Status.below)
之后,UI仍未更新
refresh()
关于这里可能发生什么的任何想法?
预先感谢!
在@Asperi回答后进行编辑,我的func refresh() {
self.worklog = self.jiraService.getCounter(tickets: Int.random(in: 3..<5), minutes: 390, status: "OK")
print("Now mocked is: \(self.worklog)")
self.timeAndTicketsMsg = "You logged \(String(format: "%.2f", Double(self.worklog.minutesLogged) / 60.0)) hours in \(self.worklog.totalTickets) tickets today"
print(self.timeAndTicketsMsg)
self.statusMsg = "You are on \"\(self.worklog.status.rawValue)\" status"
print(statusMsg)
}
函数看起来像这样
At MockedService: {"totalTickets":4,"minutesLogged":390,"status":"OK"}
Now mocked is: Worklog(minutesLogged: 390, totalTickets: 4, status: JiraWorkflows.Status.below)
You logged 6.50 hours in 4 tickets today
You are on "below" status
哪些印刷品:
ObservableObject
这是正确的。但是,UI不会刷新。我还更改了final class JiraData: ObservableObject {
// rest of the class
var jiraService = MockedService()
var worklog: Worklog
@Published var timeAndTicketsMsg: String
@Published var statusMsg: String
}
,现在看起来像这样:
ContentView
现在,我的@ObservedObject var jiraData = JiraData()
var body: some View {
VStack(alignment: .leading, spacing: 0) {
Text(jiraData.timeAndTicketsMsg)
// rest of the Text
Text(jiraData.statusMsg)
// rest of the class
}
如下所示:
with base_line( min_price, avg_price, max_price) as
(select min(list_price), avg(list_price), max(list_price)
from products
)
select sum(cheap) chaep, sum(fair) fair, sum(expensive) expensive
from (select case when list_price < (avg_price - min_price) / 2 then 1 else 0 end as cheap
, case when list_price > (max_price - avg_price) / 2 then 1 else 0 end as fair
, case when not list_price < (avg_price - min_price) / 2
and not list_price > (max_price - avg_price) / 2 then 1 else 0 end as expensive
from products
cross join base_line
) w;
答案 0 :(得分:2)
问题出在调用函数上,例如
Text(jiraData.getTimeAndTickets())
当属性更改和视图主体之间存在依赖关系时,刷新发生。无法跟踪功能的更改。
所以您需要将代码重构为那种类型
Text(jiraData.timeAndTickets)
timeAndTickets
在哪里
@Published var timeAndTickets: String
,只要执行refresh
,您就可以直接对其进行修改...。然后相应地更新视图。