SwiftUI ObservableObject无法刷新视图上的数据

时间:2020-11-10 00:03:48

标签: swift cocoa swiftui

我正在尝试使用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

这时,我的应用看起来像这样(正确)。

enter image description here

我知道,到目前为止,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;

1 个答案:

答案 0 :(得分:2)

问题出在调用函数上,例如

   Text(jiraData.getTimeAndTickets())

当属性更改和视图主体之间存在依赖关系时,刷新发生。无法跟踪功能的更改。

所以您需要将代码重构为那种类型

   Text(jiraData.timeAndTickets)

timeAndTickets在哪里

@Published var timeAndTickets: String

,只要执行refresh,您就可以直接对其进行修改...。然后相应地更新视图。