【问题标题】:How to refresh view with fetched data - Firestore & SwiftUI如何使用获取的数据刷新视图 - Firestore 和 SwiftUI
【发布时间】:2021-05-15 16:05:39
【问题描述】:

简短:我视图中的图像在第一次加载后没有更新。该 URL 与之前加载的视图保持一致,但未从存储中获取 URL 或数据的视图的其余部分将被更新。

完整:我有两个视图,一个ListView 和一个DetailView

ListView 中,我显示List 类型的列表。详细视图应该显示来自List.profiles 的每个Profile。为此,我将每个字符串 uid 存储在 List.profiles 中并调用 model.fetchProfiles 以获取每个选定列表的配置文件。

在第一个选中的List model.fetchProfiles 返回文档,model.profilesDetailView 中显示数据。

当第一次加载DetailView 时,出现的ProfileRow 会被调用并记录获取的配置文件。然后ProfileRowimagePath 加载imageURL 并使用它来获取图像。

控制台:加载列表1

卡片出现了:个人资料 []
卡出现:SortedProfiles [] 卡片行
出现卡片行:个人资料profiles/XXXXXX/Profile/profile.png
出现卡片行:SortedProfiles profiles/XXXXXX/Profile/profile.png
从图片路径获取url:profiles/XXXXXX/Profile/profile.png
图片网址:https://firebasestorage.googleapis.com/APPNAME/profiles%XXXXXXX

当从ListView 中选择第二个List 时,ProfileRow didAppear 没有被调用;

if model.profiles.count > 0 {
  print("CARD ROW DID APPEAR: Profiles \(model.profiles[0]. imgPath)")     
  print("CARD ROW DID APPEAR: Sorted  \(model.sortedProfiles[0].imgPath)")
}

并且在ListView 中选择List 时不会再出现,但是ProfileRow 中的其余配置文件数据会显示,例如名称,因此必须获取数据。

ImagePath 与加载完全相同图像的第一个视图相同。 Profile 的所有其他属性(例如名称)均已正确加载。

控制台:加载 List2

卡片出现了:个人资料 []
卡出现:SortedProfiles [] 卡片行
从图片路径获取url:profiles/XXXXXX/Profile/profile.png 图片网址: https://firebasestorage.googleapis.com/APPNAME/profiles%XXXXXXX

如果我然后导航到List1,则出现List2 的图像,如果我重新选择List2,图像看起来很好。 The image show is correct on first load, and when selecting another list it always the one from before.

谁能帮帮我?

第一次查看

struct ListViw: View {
  @EnvironmentObject var model: Model

  var body: some View {
    VStack {
        
        ForEach(model.lists.indices, id: \.self) { index in
            NavigationLink(
                destination: DetailView()
                            .environmentObject(model)
                            .onAppear() {
                                model.fetchProfiles()
                            }
            ) {
                 ListRow(home:model.lists[index])
                    .environmentObject(model)
            }
            .isDetailLink(false)
        }
    }
  }

}

详细视图卡片

struct ProfilesCard: View {

@EnvironmentObject var model: Model

var body: some View {
    VStack(alignment: .trailing, spacing: 16) {                
            if !model.sortedProfiles.isEmpty {
                VStack(alignment: .leading, spacing: 16) {
                    ForEach(model.sortedProfiles.indices, id: \.self) { index in
                        ProfileRow(
                            name: "\(model.sortedProfiles[index].firstName) \(model.sortedProfiles[index].lastName)",
                            imgPath: model.sortedProfiles[index].imgPath,
                            index: index)
                            .environmentObject(model)
                    }
                }
                .padding(.top, 16)
            }
        
    }//End of Card
    .modifier(Card())
    .onAppear() {
        print("CARD DID APPEAR: Profiles \(model.profiles)")
        print("CARD DID APPEAR: SORTED \(model.sortedTenants)")
    }
}
}



struct ProfileRow: View {

@EnvironmentObject var model: Model

@State var imageURL = URL(string: "")

var name: String
var imgPath: String
var index: Int

private func loadImage() {
    print("load image: \(imgPath)")
    DispatchQueue.main.async {
        fm.getURLFromFirestore(path: imgPath,  success: { (imgURL) in
                   print("Image URL: \(imgURL)")
            imageURL = imgURL
               }) { (error) in
                   print(error)
        }
    }
}

var body: some View {
    VStack(alignment: .leading, spacing: 12) {
        HStack(alignment: .center, spacing: 12) {

                   KFImage(imageURL,options: [.transition(.fade(0.2)), .forceRefresh])
                       .placeholder {
                           Rectangle().foregroundColor(.gray)
                       }
                       .resizable()
                       .aspectRatio(contentMode: .fill)
                       .frame(width: 32, height: 32)
                       .cornerRadius(16)


                // Profile text is always displayed correctly
            Text(name)
                    .modifier(BodyText())
                    .frame(maxWidth: .infinity, alignment: .leading)
        }
    }
    .onAppear() {
        print("CARD ROW")

        // Crashes if check is not there
        if model.profiles.count > 0 {

            print("CARD ROW DID APPEAR: Profiles \(model.profiles[0]. imgPath)")

            print("CARD ROW DID APPEAR: Sorted  \(model.sortedProfiles[0].imgPath)")
        }
        
       loadImage()
    }
  }
}

型号

class Model: ObservableObject {

  init() {
      fetchData()
  }

  @Published var profiles: [Profile] = []
  var sortedProfiles: [Profile] {return profiles.removeDuplicates } 

  @Published var list: List? {
      didSet {
          fetchProfiles()
      }
  }

  func fetchData() {
    if let currentUser = Auth.auth().currentUser {
        
        email = currentUser.email!

            db.collection("lists")
                .whereField("createdBy", isEqualTo: currentUser.uid)
                .addSnapshotListener { (querySnapshot, error) in
                guard let documents = querySnapshot?.documents else {
                    return
                }

                self.lists = documents.compactMap { queryDocumentSnapshot -> List? in
                    return try? queryDocumentSnapshot.data(as: List.self)
                }
            }

    }
 }

  func fetchProfiles() {
    profiles.removeAll()
    
    for p in list!.profiles {
        firestoreManager.fetchProfile(uid: t, completion: { [self] profile in
            profiles.append(profile)
        })
    }
  }

}

更新

到目前为止,我尝试将didSet 用于ImgPathImgURL,但仍然没有运气。也试过直接使用model.profiles

【问题讨论】:

    标签: firebase google-cloud-firestore swiftui combine


    【解决方案1】:

    据我所知,这不是 firebase 端的问题,因为获取新数据的数据已更新。您正面临图像缓存的问题。缓存是一种存储给定资源副本的技术。因此,当第一次加载图像时,它会被缓存,并且无论何时重新加载图像都会从缓存中显示,而不是从 URL 加载。这样做是为了更多的网络使用。

    您可以通过在图像加载之前添加以下代码以编程方式清除缓存。

    Alamofire 在后台使用 NSURLCache,所以你只需要调用:

    NSURLCache.sharedURLCache().removeAllCachedResponses()
    

    Swift 4.1 更新

    URLCache.shared.removeAllCachedResponses()
    

    【讨论】:

    • 我希望这可以解决您的问题。如果您仍有问题,可以在这里联系我
    【解决方案2】:

    在使用 Firestore API 的所有回调中,对主队列上的已发布或状态属性进行分配,因为可能会在后台队列上调用回调。

    所以,假设数据被正确返回和解析,它应该是这样的

    for p in list!.profiles {
        firestoreManager.fetchProfile(uid: t, completion: { [self] profile in
            DispatchQueue.main.async {
               profiles.append(profile)
            }
        })
    }
    

    我还建议避免使用 SDK 类型对自定义类型进行相同命名 - 可能会有非常令人困惑的非明显错误

    // List model below might conflict with SwiftUI List
    return try? queryDocumentSnapshot.data(as: List.self)
    

    【讨论】:

    猜你喜欢
    • 2020-08-05
    • 2021-07-08
    • 2022-12-07
    • 2017-12-22
    • 1970-01-01
    • 1970-01-01
    • 2021-01-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多