刷新从 HTTP 请求中提取的 SwiftUI 视图中的数据
Posted
技术标签:
【中文标题】刷新从 HTTP 请求中提取的 SwiftUI 视图中的数据【英文标题】:Refresh Data in SwiftUI View pulled from HTTP Request 【发布时间】:2020-07-09 15:54:38 【问题描述】:我一直在寻找解决此问题的方法,但我发现的方法似乎都无法解决我的具体情况。希望有了这些细节,有人可以帮助我。我是 Swift 的新手,所以请耐心等待。我正在使用来自我们的服务台票证系统的 REST Api 和 HTTP 请求。我正在尝试找到一种在某些情况发生变化时自动刷新数据的方法,并且还有一种手动刷新的方法,即下拉或实际刷新按钮。如果我能在下面的两个场景中弄清楚这一点,我想我可以将它应用到我的其余场景中。
场景 #1 - 我为工单提交了新注释,但详细视图并未更改以反映该新注释。我必须返回并重新打开视图才能看到更改。
获取工单详情
struct TicketDetails: Codable, Identifiable
var id: Int
var type: String
var location: Location
var detail: String
var notes: [Notes]
var statustype: StatusType
struct Location: Codable
let locationName: String
struct Notes: Codable
var prettyUpdatedString: String?
var mobileNoteText: String?
struct StatusType: Codable
var id: Int
var statusTypeName: String
class FetchTick: ObservableObject
func getTicket(id: Int, userApi: String, completion: @escaping (TicketDetails) -> ())
guard let url = URL(string: "URL FOR TICKET DATA") else return
URLSession.shared.dataTask(with: url) (data, _, _) in
let ticket = try! JSONDecoder().decode(TicketDetails.self, from: data!)
DispatchQueue.main.async
completion(ticket)
.resume()
在工单上创建新注释
class CreateTicketNote: ObservableObject
func CreateNoteAction(ticketId: Int, userApi: String, techNote: String)
let ticketUrl = URL(string:
"URLFORTICKET")!
var request = URLRequest(url: ticketUrl)
request.httpMethod = "POST"
let json: [String: Any] = [
"noteText": techNote,
"jobticket": [
"id": ticketId,
"type": "Ticket"
]
]
let data = try! JSONSerialization.data(withJSONObject: json, options: .prettyPrinted)
URLSession.shared.uploadTask(with: request, from: data) (responseData, response, error) in
if let error = error
print("Error making POST request: \(error.localizedDescription)")
return
if let responseCode = (response as? HTTPURLResponse)?.statusCode, let responseData = responseData
guard responseCode == 200 else
print("Invalid response code: \(responseCode)")
return
if let responseJSONData = try? JSONSerialization.jsonObject(with: responseData, options: .allowFragments)
print("Response JSON data = \(responseJSONData)")
.resume()
详细视图
struct DetailsView: View
@ObservedObject var ticketStatusAction = TicketStatusAction()
@ObservedObject var createTicketNote = CreateTicketNote()
@State var ticket: [TicketDetails] = []
@State private var showingNoteAlert = false
@State private var showingOpenAlert = false
@State private var showingPendingAlert = false
@State private var showingDepotAlert = false
@State private var showingCloseAlert = false
@State private var note: String = ""
var id: Int
var displayClient: String
@Binding var userApi: String
var body: some View
ScrollView(.vertical)
VStack(alignment: .leading)
if !ticket.isEmpty
Text(self.ticket.first?.location.locationName ?? "")
.fontWeight(.bold)
.padding()
Text("\(displayClient) - \(id)")
.fontWeight(.bold)
.font(.system(size:20))
.padding()
Divider()
Text("Status")
.fontWeight(.bold)
.padding()
if !ticket.isEmpty
Text(self.ticket.first?.statustype.statusTypeName ?? "")
.padding()
Text("Details")
.fontWeight(.bold)
.padding()
if !ticket.isEmpty
Text(clearMarkdown(on:self.ticket.first?.detail ?? ""))
.padding()
.fixedSize(horizontal: false, vertical: true)
Divider()
Text("Most Recent Note")
.fontWeight(.bold)
.padding()
if !ticket.isEmpty
Text(clearMarkdown(on: self.ticket.first?.notes.first?.prettyUpdatedString ?? ""))
.padding()
Text(clearMarkdown(on: self.ticket.first?.notes.first?.mobileNoteText ?? ""))
.padding()
.fixedSize(horizontal: false, vertical: true)
.onAppear
FetchTick().getTicket(id: self.id, userApi: self.userApi) (ticketDetails) in
self.ticket = [ticketDetails]
Divider()
Section(header: Text("Create New Note")
.fontWeight(.bold)
.padding()
.padding(10)
.frame(maxWidth: .infinity))
TextField("Enter your note", text: $note)
.textFieldStyle(RoundedBorderTextFieldStyle())
.frame(width: 350)
.padding(15)
Button(action:
self.showingNoteAlert = true
)
Text("Submit Note")
.frame(width: 300)
.padding(15)
.foregroundColor(Color.white)
.background(Color.orange)
.cornerRadius(5)
.buttonStyle(BorderlessButtonStyle()
).actionSheet(isPresented:self.$showingNoteAlert)
ActionSheet(
title: Text("Are you sure you want to add this note to \(displayClient)'s ticket?"),
message: Text("\(self.note)"),
buttons: [
.default(Text("Submit"))
self.createTicketNote.CreateNoteAction(ticketId: self.id, userApi: self.userApi, techNote: self.note);
self.note = ""
,
.cancel()
self.note = ""
])
Divider()
Section(header: Text("Change Ticket Status")
.fontWeight(.bold)
.padding()
.padding(10)
.frame(maxWidth: .infinity))
Button(action:
self.showingOpenAlert = true
)
Text("Open")
.frame(width: 300)
.padding(15)
.foregroundColor(Color.white)
.background(Color.green)
.cornerRadius(5)
.buttonStyle(BorderlessButtonStyle()).alert(isPresented:self.$showingOpenAlert)
Alert(
title: Text("Are you sure you want change \(displayClient)'s ticket to Open?"),
primaryButton: .default(Text("Open"))
self.ticketStatusAction.TicketAction(ticketId: self.id, userApi: self.userApi, desiredStatus: 1)
,
secondaryButton: .cancel())
Spacer()
Button(action:
self.showingPendingAlert = true
)
Text("Pending")
.frame(width: 300)
.padding(15)
.foregroundColor(Color.white)
.background(Color.yellow)
.cornerRadius(5)
.buttonStyle(BorderlessButtonStyle()).alert(isPresented:self.$showingPendingAlert)
Alert(
title: Text("Are you sure you want to set \(displayClient)'s ticket to Pending?"),
primaryButton: .default(Text("Pending"))
self.ticketStatusAction.TicketAction(ticketId: self.id, userApi: self.userApi, desiredStatus: 2)
,
secondaryButton: .cancel())
Spacer()
Button(action:
self.showingDepotAlert = true
)
Text("Depot")
.frame(width: 300)
.padding(15)
.foregroundColor(Color.white)
.background(Color.blue)
.cornerRadius(5)
.buttonStyle(BorderlessButtonStyle()).alert(isPresented:self.$showingDepotAlert)
Alert(
title: Text("Are you sure you want to depot \(displayClient)'s ticket?"),
primaryButton: .default(Text("Depot"))
self.ticketStatusAction.TicketAction(ticketId: self.id, userApi: self.userApi,
desiredStatus: 6)
,
secondaryButton: .cancel())
Spacer()
Button(action:
self.showingCloseAlert = true
)
Text("Close")
.frame(width: 300)
.padding(15)
.foregroundColor(Color.white)
.background(Color.red)
.cornerRadius(5)
.buttonStyle(BorderlessButtonStyle()).alert(isPresented:self.$showingCloseAlert)
Alert(
title: Text("Are you sure you want to close \(displayClient)'s ticket?"),
primaryButton: .destructive(Text("Close"))
self.ticketStatusAction.TicketAction(ticketId: self.id, userApi: self.userApi, desiredStatus: 3)
,
secondaryButton: .cancel())
Spacer()
场景 #2 - 我正在查看票证列表,并希望确保没有其他票证已打开。我想拉刷新列表并显示任何新票。
场地门票
struct TicksByLocation: Codable, Identifiable
public var id: Int
public var type: String
public var displayClient: String
public var shortDetail: String
class FetchTicksByLocation: ObservableObject
func getTicksByLocation(ticketLocation: String, userApi: String, completion: @escaping (([TicksByLocation]) -> ()))
guard let url = URL(string: "URLFORTICKETS") else
return
URLSession.shared.dataTask(with: url) (data, response, error) in
do
if let tickData = data
let decodedData = try JSONDecoder().decode([TicksByLocation].self, from: tickData)
DispatchQueue.main.async
completion(decodedData)
else
print("No data")
catch
print("Error")
.resume()
按位置显示门票
struct ShowLocationView: View
@Binding var ticketLocation: String
@Binding var userApi: String
@Binding var tickets: [TicksByLocation]
@State var openTickets: [TicksByStatusAndLocation] = []
@State var green = Color.green
@State var yellow = Color.yellow
@State var blue = Color.blue
@State var purple = Color.purple
var body: some View
NavigationView
List(tickets) tick in
VStack(alignment: .leading, spacing: 10)
Text("\(tick.id)")
.font(.system(size: 11))
.cornerRadius(5)
Text(tick.displayClient)
Text(tick.shortDetail)
.font(.system(size: 11))
.foregroundColor(Color.gray)
NavigationLink(destination: DetailsView(
id: tick.id,
displayClient: tick.displayClient,
userApi: self.$userApi
))
Text("See Details")
.foregroundColor(Color.blue)
Divider()
.navigationBarTitle(
Text("\(ticketLocation) - All (\(tickets.count))"),
displayMode: .inline
)
.navigationBarItems(
trailing:
Text("Filter")
.contextMenu
NavigationLink(
destination: ShowLocationAndStatusView(
ticketLocation: $ticketLocation,
userApi: $userApi,
status: "Open",
color: green
)
)
Text("Open")
NavigationLink(
destination: ShowLocationAndStatusView(
ticketLocation: $ticketLocation,
userApi: $userApi,
status: "Pending",
color: yellow
)
)
Text("Pending")
NavigationLink(
destination: ShowLocationAndStatusView(
ticketLocation: $ticketLocation,
userApi: $userApi,
status: "Depot",
color: blue
)
)
Text("Depot")
NavigationLink(
destination: ShowLocationAndStatusView(
ticketLocation: $ticketLocation,
userApi: $userApi,
status: "Agi",
color: purple
)
)
Text("Agi")
)
【问题讨论】:
【参考方案1】:解决方案 #1
Section(header: Text("Create New Note")
.fontWeight(.bold)
.padding()
.padding(10)
.frame(maxWidth: .infinity))
TextField("Enter your note", text: $note)
.textFieldStyle(RoundedBorderTextFieldStyle())
.frame(width: 350)
.padding(15)
Button(action:
self.showingNoteAlert = true
)
Text("Submit Note")
.frame(width: 300)
.padding(15)
.foregroundColor(Color.white)
.background(Color.orange)
.cornerRadius(5)
.buttonStyle(BorderlessButtonStyle()
).actionSheet(isPresented:self.$showingNoteAlert)
ActionSheet(
title: Text("Are you sure you want to add this note to \(displayClient)'s ticket?"),
message: Text("\(self.note)"),
buttons: [
.default(Text("Submit"))
self.createTicketNote.CreateNoteAction(ticketId: self.id, userApi: self.userApi, techNote: self.note);
self.note = "";
DispatchQueue.main.asyncAfter(deadline: .now() + 1)
FetchTick().getTicket(id: self.id, userApi: self.userApi) (ticketDetails) in
self.ticket = [ticketDetails]
,
.cancel()
self.note = ""
])
解决方案 #2
使用https://github.com/phuhuynh2411/SwiftUI-PullToRefresh
import SwiftUI
import UIKit
struct ShowSelectedStatus: View
@State private var isShowing = false
@Binding var userApi: String
@Binding var tickets: [TicksByStatus]
var ticketStatus: String
var color: Color
var body: some View
NavigationView
List(tickets) tick in
VStack(alignment: .leading, spacing: 10)
Text(tick.displayClient)
.padding(10)
.background(self.color)
.cornerRadius(5)
Text(tick.shortDetail)
.font(.system(size: 11))
.foregroundColor(Color.gray)
NavigationLink(destination: DetailsView(
id: tick.id,
displayClient: tick.displayClient,
userApi: self.$userApi
))
Text("See Details")
.foregroundColor(Color.blue)
Divider()
.pullToRefresh(isShowing: $isShowing)
DispatchQueue.main.asyncAfter(deadline: .now() + 1)
self.isShowing = false
FetchTicksByStatus().getTicksByStatus(
ticketStatus: self.ticketStatus,
userApi: self.userApi
)
(ticks) in self.tickets = ticks
.navigationBarTitle(
Text("\(ticketStatus) (\(tickets.count))"),
displayMode: .inline
)
【讨论】:
以上是关于刷新从 HTTP 请求中提取的 SwiftUI 视图中的数据的主要内容,如果未能解决你的问题,请参考以下文章
如何以编程方式从 SwiftUI 列表中删除行并刷新列表视图?
SwiftUI 4.0(iOS 16+)使用新的 Gauge 视图极简实现仪表盘外观