仅在使用 Completion Handler 解决异步后更新视图
Update View Only After Aync Is Resolved with Completion Handler
我正在尝试更新我的视图,只有在解决异步调用之后。在下面的代码中,arrayOfTodos.items
在呈现 TodoListApp
之后稍微异步进入。我遇到的问题是,当 onAppear
运行时,self.asyncTodoList.items
总是 为空,因为它还没有从网络调用中接收到数组的值.我一直在试图弄清楚如何推迟 运行 onAppear
直到 Promise 解决之后,就像使用完成处理程序一样?并根据网络调用的结果,再修改视图。谢谢你的帮助!我坚持这个的时间比我承认的还要长!
struct ContentView: View {
@StateObject var arrayOfTodos = AsyncGetTodosNetworkCall()
var body: some View {
TodoListApp(asyncTodoList: arrayOfTodos)
}
}
struct TodoListApp: View {
@ObservedObject var asyncTodoList: AsyncGetTodosNetworkCall
@State private var showPopUp: Bool = false
var body: some View {
NavigationView {
ZStack {
VStack {
Text("Top Area")
Text("List Area")
}
if self.showPopUp == true {
VStack {
Text("THIS IS MY POPUP!")
Text("No Items Added Yet")
}.frame(width: 300, height: 400)
}
}.onAppear {
let arrayItems = self.asyncTodoList
if arrayItems.items.isEmpty {
self.showPopUp = true
}
/*HERE! arrayItems.items.isEmpty is ALWAYS empty when onAppear
runs since it's asynchronous. What I'm trying to do is only
show the popup if the array is empty after the promise is
resolved.
What is happening is even if array resolved with multiple todos,
the popup is still showing because it was initially empty on
first run. */
}
}
}
}
class AsyncGetTodosNetworkCall: ObservableObject {
@AppStorage(DBUser.userID) var currentUserId: String?
private var REF_USERS = DB_BASE.collection(DBCOLLECTION.appUsers)
@Published var items = [TodoItem]()
func fetchTodos(toDetach: Bool) {
guard let userID = currentUserId else {
return
}
let userDoc = REF_USERS.document(String(userID))
.collection(DBCOLLECTION.todos)
.addSnapshotListener({ (querySnapshot, error) in
guard let documents = querySnapshot?.documents else {
print("No Documents Found")
return
}
self.items = documents.map { document -> TodoItem in
let todoID = document.documentID
let todoName = document.get(ToDo.todoName) as? String ?? ""
let todoCompleted = document.get(Todo.todoCompleted) as? Bool ?? false
return TodoItem(
id: todoID,
todoName: todoName,
todoCompleted: todoCompleted
)
}
})
if toDetach == true {
userDoc.remove()
}
}
}
在准备我的问题时,我找到了自己的答案。这是为了防止有人在路上 运行 遇到同样的问题。
struct ContentView: View {
@StateObject var arrayOfTodos = AsyncGetTodosNetworkCall()
@State var hasNoTodos: Bool = false
func getData() {
self.arrayOfTodos.fetchTodos(toDetach: false) { noTodos in
if noTodos {
self.hasNoTodos = true
}
}
}
func removeListeners() {
self.arrayOfTodos.fetchTodos(toDetach: true)
}
var body: some View {
TabView {
TodoListApp(asyncTodoList: arrayOfTodos, hasNoTodos : self.$hasNoTodos)
}.onAppear(perform: {
self.getData()
}).onDisappear(perform: {
self.removeListeners()
})
}
}
struct TodoListApp: View {
@ObservedObject var asyncTodoList: AsyncGetTodosNetworkCall
@Binding var hasNoTodos: Bool
@State private var hidePopUp: Bool = false
var body: some View {
NavigationView {
ZStack {
VStack {
Text("Top Area")
ScrollView {
LazyVStack {
ForEach(asyncTodoList.items) { item in
HStack {
Text("\(item.name)")
Spacer()
Text("Value")
}
}
}
}
}
if self.hasNoTodos == true {
if self.hidePopUp == false {
VStack {
Text("THIS IS MY POPUP!")
Text("No Items Added Yet")
}.frame(width: 300, height: 400)
}
}
}
}
}
}
class AsyncGetTodosNetworkCall: ObservableObject {
@AppStorage(DBUser.userID) var currentUserId: String?
private var REF_USERS = DB_BASE.collection(DBCOLLECTION.appUsers)
@Published var items = [TodoItem]()
func fetchTodos(toDetach: Bool, handler: @escaping (_ noTodos: Bool) -> ()) {
guard let userID = currentUserId else {
handler(true)
return
}
let userDoc = REF_USERS.document(String(userID))
.collection(DBCOLLECTION.todos)
.addSnapshotListener({ (querySnapshot, error) in
guard let documents = querySnapshot?.documents else {
print("No Documents Found")
handler(true)
return
}
self.items = documents.map { document -> TodoItem in
let todoID = document.documentID
let todoName = document.get(ToDo.todoName) as? String ?? ""
let todoCompleted = document.get(Todo.todoCompleted) as? Bool ?? false
return TodoItem(
id: todoID,
todoName: todoName,
todoCompleted: todoCompleted
)
}
handler(false)
})
if toDetach == true {
userDoc.remove()
}
}
}
我正在尝试更新我的视图,只有在解决异步调用之后。在下面的代码中,arrayOfTodos.items
在呈现 TodoListApp
之后稍微异步进入。我遇到的问题是,当 onAppear
运行时,self.asyncTodoList.items
总是 为空,因为它还没有从网络调用中接收到数组的值.我一直在试图弄清楚如何推迟 运行 onAppear
直到 Promise 解决之后,就像使用完成处理程序一样?并根据网络调用的结果,再修改视图。谢谢你的帮助!我坚持这个的时间比我承认的还要长!
struct ContentView: View {
@StateObject var arrayOfTodos = AsyncGetTodosNetworkCall()
var body: some View {
TodoListApp(asyncTodoList: arrayOfTodos)
}
}
struct TodoListApp: View {
@ObservedObject var asyncTodoList: AsyncGetTodosNetworkCall
@State private var showPopUp: Bool = false
var body: some View {
NavigationView {
ZStack {
VStack {
Text("Top Area")
Text("List Area")
}
if self.showPopUp == true {
VStack {
Text("THIS IS MY POPUP!")
Text("No Items Added Yet")
}.frame(width: 300, height: 400)
}
}.onAppear {
let arrayItems = self.asyncTodoList
if arrayItems.items.isEmpty {
self.showPopUp = true
}
/*HERE! arrayItems.items.isEmpty is ALWAYS empty when onAppear
runs since it's asynchronous. What I'm trying to do is only
show the popup if the array is empty after the promise is
resolved.
What is happening is even if array resolved with multiple todos,
the popup is still showing because it was initially empty on
first run. */
}
}
}
}
class AsyncGetTodosNetworkCall: ObservableObject {
@AppStorage(DBUser.userID) var currentUserId: String?
private var REF_USERS = DB_BASE.collection(DBCOLLECTION.appUsers)
@Published var items = [TodoItem]()
func fetchTodos(toDetach: Bool) {
guard let userID = currentUserId else {
return
}
let userDoc = REF_USERS.document(String(userID))
.collection(DBCOLLECTION.todos)
.addSnapshotListener({ (querySnapshot, error) in
guard let documents = querySnapshot?.documents else {
print("No Documents Found")
return
}
self.items = documents.map { document -> TodoItem in
let todoID = document.documentID
let todoName = document.get(ToDo.todoName) as? String ?? ""
let todoCompleted = document.get(Todo.todoCompleted) as? Bool ?? false
return TodoItem(
id: todoID,
todoName: todoName,
todoCompleted: todoCompleted
)
}
})
if toDetach == true {
userDoc.remove()
}
}
}
在准备我的问题时,我找到了自己的答案。这是为了防止有人在路上 运行 遇到同样的问题。
struct ContentView: View {
@StateObject var arrayOfTodos = AsyncGetTodosNetworkCall()
@State var hasNoTodos: Bool = false
func getData() {
self.arrayOfTodos.fetchTodos(toDetach: false) { noTodos in
if noTodos {
self.hasNoTodos = true
}
}
}
func removeListeners() {
self.arrayOfTodos.fetchTodos(toDetach: true)
}
var body: some View {
TabView {
TodoListApp(asyncTodoList: arrayOfTodos, hasNoTodos : self.$hasNoTodos)
}.onAppear(perform: {
self.getData()
}).onDisappear(perform: {
self.removeListeners()
})
}
}
struct TodoListApp: View {
@ObservedObject var asyncTodoList: AsyncGetTodosNetworkCall
@Binding var hasNoTodos: Bool
@State private var hidePopUp: Bool = false
var body: some View {
NavigationView {
ZStack {
VStack {
Text("Top Area")
ScrollView {
LazyVStack {
ForEach(asyncTodoList.items) { item in
HStack {
Text("\(item.name)")
Spacer()
Text("Value")
}
}
}
}
}
if self.hasNoTodos == true {
if self.hidePopUp == false {
VStack {
Text("THIS IS MY POPUP!")
Text("No Items Added Yet")
}.frame(width: 300, height: 400)
}
}
}
}
}
}
class AsyncGetTodosNetworkCall: ObservableObject {
@AppStorage(DBUser.userID) var currentUserId: String?
private var REF_USERS = DB_BASE.collection(DBCOLLECTION.appUsers)
@Published var items = [TodoItem]()
func fetchTodos(toDetach: Bool, handler: @escaping (_ noTodos: Bool) -> ()) {
guard let userID = currentUserId else {
handler(true)
return
}
let userDoc = REF_USERS.document(String(userID))
.collection(DBCOLLECTION.todos)
.addSnapshotListener({ (querySnapshot, error) in
guard let documents = querySnapshot?.documents else {
print("No Documents Found")
handler(true)
return
}
self.items = documents.map { document -> TodoItem in
let todoID = document.documentID
let todoName = document.get(ToDo.todoName) as? String ?? ""
let todoCompleted = document.get(Todo.todoCompleted) as? Bool ?? false
return TodoItem(
id: todoID,
todoName: todoName,
todoCompleted: todoCompleted
)
}
handler(false)
})
if toDetach == true {
userDoc.remove()
}
}
}