修改 viewModel 时跳转的 SwiftUI 视图

SwiftUI views jumping around when modifying viewModel

提问人:Dimitri Borgers 提问时间:11/15/2023 更新时间:11/15/2023 访问量:36

问:

我正在尝试创建一个允许用户接受或拒绝好友请求的 fullScreenCover。但是,每当我接受好友请求时,视图都会多次更改。这是由于在两个不同的视图之间共享一个 viewModel 吗?

问题(请注意,单击绿色复选标记后,之后发生的一切都发生在我没有触摸屏幕的情况下):

enter image description here

FriendsView.swift:

import SwiftUI

struct FriendsView: View {
    
    @State private var showAddFriendView = false
    @EnvironmentObject private var viewModel: AuthenticationModel
    
    var body: some View {
        NavigationStack {
            if viewModel.currentUser?.friends != nil && viewModel.currentUser!.friends!.contains(where: { $0.status == "ACCEPTED" }) {
                List (viewModel.currentUser!.friends ?? []) { friend in
                    if friend.status == "ACCEPTED" {
                        NavigationLink(value: friend) {
                            Text(friend.username)
                        }
                    }
                }
            } else {
                VStack() {
                    Text("To start sharing seecrets, first invite your friends!")
                    
                    Button {
                        showAddFriendView.toggle()
                    } label: {
                        Text("Add friends")
                    }.fullScreenCover(isPresented: $showAddFriendView) {
                        AddFriendView().environmentObject(viewModel)
                    }
                }
            }
        }
    }
}

添加朋友视图.swift:

import SwiftUI
import FirebaseFirestoreSwift
import FirebaseFirestore
import FirebaseFunctions

struct AddFriendView: View {
    @State private var friendUsername: String = ""
    @EnvironmentObject private var viewModel: AuthenticationModel
    @State private var isLoading: Bool = false
    @State private var errorText: String = ""
    @Environment(\.dismiss) var dismiss
        
    func sendFriendRequest() {
        // NOT IMPORTANT
    }
    
    func acceptFriendRequest(acceptRequestFriendUsername: String) {
        errorText = ""
        isLoading = true
                
        let functions = Functions.functions()
        
        // Call the function, passing in the username you want to check
        functions.httpsCallable("acceptFriendRequest").call(["friendUsername": acceptRequestFriendUsername]) { (result, error) in
            
            // Handle successful response
            if let response = result?.data as? [String: Bool] {
                if let updated = response["success"], updated {
                    
                    // Find the user you just accepted and change friend request status
                    for index in 0..<(viewModel.currentUser?.friends!.count)! {
                        if viewModel.currentUser?.friends?[index].username == acceptRequestFriendUsername {
                            DispatchQueue.main.async {
                                viewModel.currentUser?.friends?[index].status = "ACCEPTED"
                            }
                            break
                        }
                    }
                    
                    DispatchQueue.main.async {
                        showToastRequestAccepted = true
                        isLoading = false
                    }
                }
            }
        }
        isLoading = false
    }
    
    func denyFriendRequest(denyRequestFriendUsername: String) {
        // NOT IMPORTANT
    }
    
    var body: some View {
        NavigationStack {
            ZStack {
                VStack(spacing:20) {
                    
                    // NOT IMPORTANT
                                        
                    if viewModel.currentUser?.friends != nil && viewModel.currentUser!.friends!.contains(where: { $0.status == "PENDING_YOUR_RESPONSE" }) {
                        List(viewModel.currentUser!.friends ?? []) { friend in
                            if friend.status == "PENDING_YOUR_RESPONSE" {
                                HStack {
                                    Text(friend.username)
                                                                        
                                    Button(action: {
                                        denyFriendRequest(denyRequestFriendUsername: friend.username)
                                    }, label: {
                                        Image(systemName: "multiply")
                                    })
                                    .disabled(isLoading)
                                    
                                    Button(action: {
                                        acceptFriendRequest(acceptRequestFriendUsername: friend.username)
                                    }, label: {
                                        Image(systemName: "checkmark")
                                    })
                                    .disabled(isLoading)
                                }
                            }
                        }
                    } else {
                        // NOT IMPORTANT
                    }
                }
            }
            .navigationTitle("Add friends")
        }
    }
}
iOS Swift SwiftUI 并发

评论

0赞 lorem ipsum 11/15/2023
您所经历的可能是因为反对引用类型 ViewModel 的主要论点。当您进行更改时,整个视图将失效,而不是通过值类型进行有针对性的更改。
0赞 Paulw11 11/15/2023
我认为您的问题可能与这两个调用有关。这些将按顺序运行。发送/接受/拒绝好友函数不属于您的视图。您应该采用 async/await,调用 'await self.viewModel.acceptFriendRequest()',然后更新您的视图。你还有一个接受好友函数的底部,它执行我需要自己,因为你的其他代码是异步的,所以没有做你想做的事。DispatchQueue.main.asyncisLoading=falseisLoading
0赞 Paulw11 11/15/2023
您可能还需要考虑重构 Firestore 数据。与其在用户上有一个数组属性,不如在用户下有一个包含好友文档的集合。这样,您就可以更改特定朋友的状态,而无需实际更新用户文档。它还允许您避免遍历数组索引friendsfriends

答: 暂无答案