由于某种原因,当我向子视图添加
@Query
宏时,它无法渲染。如果我通过仅添加纯文本字段来简化子视图,同时仍然保留 @Query
宏,它仍然不会渲染。但是,如果我在子视图中注释掉 @Query 宏,那么它就会呈现,为什么?
我一直在挠头试图弄清楚这一点。我希望能够使用从子视图中的父视图传递的相册 id 来过滤媒体的
@Query
,但从那以后我一直无法弄清楚如何做到这一点如果我的子视图使用 @Query
宏,我将无法查看它。我觉得它可能陷入了循环或其他什么。仅供参考,我的数据库中只有大约 30 行媒体,因此它并不大。另外,如果我使用 .onAppear
修饰符将日志记录添加到子视图,即使未显示视图,它也会输出日志。
旁注,子视图的预览工作正常,只是从父视图导航到它时不行。
这是我的 SwiftData 模型:
import SwiftUI
import SwiftData
@Model
class Album: Identifiable {
var id: UUID = UUID()
var isPublicAlbum: Bool = false
var title: String = ""
var timeStamp: Date = Date()
@Relationship(deleteRule: .cascade, inverse: \Media.album) var media: [Media]?
init(isPublicAlbum: Bool, title: String, timeStamp: Date = Date()) {
self.isPublicAlbum = isPublicAlbum
self.title = title
self.timeStamp = timeStamp
}
}
import SwiftUI
import SwiftData
@Model
class Media: Identifiable {
var id: UUID = UUID()
var albumID: UUID = UUID()
@Attribute(.externalStorage) var asset: Data? = nil
var isCover: Bool = false
var contentType: String = ""
var timeStamp: Date = Date()
//var album: Album?
@Relationship(deleteRule: .nullify) var album: Album?
init(albumID: UUID = UUID(), asset: Data? = nil, isCover: Bool = false, contentType: String = "", timeStamp: Date = Date()) {
self.albumID = albumID
self.asset = asset
self.isCover = isCover
self.contentType = contentType
self.timeStamp = timeStamp
}
}
这是我的模型容器:
import SwiftData
class ModelContainerManager {
static let shared = ModelContainerManager()
let container: ModelContainer
private init() {
do {
container = try ModelContainer(for: User.self, Album.self, Media.self)
} catch {
fatalError(error.localizedDescription)
}
}
}
我将我的容器传递给我的所有视图,就像这样:
WindowGroup {
//...
}
.modelContainer(ModelContainerManager.shared.container)
这是我的父视图的相关代码:
struct MainView: View {
@Environment(\.modelContext) private var dbContext
@Environment(\.horizontalSizeClass) var horizontalSizeClass
@Environment(\.colorScheme) var colorScheme
@Query(filter: #Predicate<Album> { $0.isPublicAlbum == false }, sort: \Album.title, order: .forward) private var listAlbums: [Album]
@State private var searchString: String = ""
private var filteredAlbums: [Album] {
if searchString.isEmpty {
return listAlbums
} else {
return listAlbums.filter { album in
album.title.localizedStandardContains(searchString)
}
}
}
//...
var body: some View {
VStack {
CustomSearchBar(text: $searchString, placeholder: NSLocalizedString("Search Private Albums", comment: "This is a search bar to search for private albums"))
//...
ForEach(filteredAlbums) { album in
VStack(spacing: 0) {
ZStack(alignment: .topTrailing) {
NavigationLink(destination: PersistentAlbumView(album: album)) {
//...
}
}
}
}
//...
}
}
}
这是我的孩子视图:
import SwiftUI
import SwiftData
struct PersistentAlbumView: View {
@Environment(\.modelContext) private var dbContext
@Query private var mediaItems: [Media]
let album: Album
var body: some View {
VStack {
if !mediaItems.isEmpty {
List(mediaItems) { item in
if item.albumID == album.id {
Text("Media ID: \(item.id.uuidString)")
}
}
} else {
Text("No media found")
}
}
}
}
#Preview {
NavigationStack {
PersistentAlbumView(album: PreviewContainer.getAlbum(at: 3))
.modelContainer(PreviewContainer.container)
.navigationBarTitleDisplayMode(.inline)
}
}
我通过在父视图之外使用以下简化视图来测试它,只是为了确保媒体查询确实有效,而且它确实有效:
import SwiftUI
import SwiftData
struct MediaListView: View {
@Query private var mediaItems: [Media]
var body: some View {
List(mediaItems, id: \.id) { media in
Text(media.id.uuidString)
}
}
}
#Preview {
MediaListView()
.modelContainer(PreviewContainer.container)
}
如果有人能告诉我我到底做错了什么,那就太棒了!
附注我不想使用
album.media
因为它是一个关系并且被视为计算属性。使用 album.media
时,重新安装应用程序且新数据已与我的 Media
模型同步时,视图不会实时显示更新。所以利用这种关系对我来说不是一个选择。
任何有关修复我的子视图中的
@Query
宏以在我的 Media
模型上使用它的帮助都会很棒!我通常可以弄清楚这样的事情,但我不习惯使用 SwiftData。预先感谢!
我花了一个多星期的时间到处修补这个问题,因为我有时间,奇怪的是,我在发布问题后能够解决这个问题。我没有删除问题,而是想分享解决方案和问题,以帮助其他人在多个子视图中使用 SwiftData
@Query
宏时避免这个巨大的麻烦。
这个问题确实是一个无限循环,当您在父视图中拥有多个子视图
NavigationStack
并且您使用 NavigationLink
来渲染那些使用附加 @Query
宏的子视图时,会导致该问题。
我的解决方案是首先在使用第一个
struct
宏的父视图中创建一个新的 @Query
,然后从那里渲染子视图。
基于我原来的帖子的示例:
import SwiftUI
import SwiftData
struct MainView: View {
@Query(filter: #Predicate<Album> { $0.isPublicAlbum == false }, sort: \Album.title, order: .forward) private var listAlbums: [Album]
//...
NavigationLink(destination: RenderAlbumView(album: album)) {
//...
}
}
//...
// Fixes NavigationStack @Query infinite loop issue
struct RenderAlbumView: View {
let album: Album
var body: some View {
PersistentAlbumView(album: album)
}
}
import SwiftUI
import SwiftData
struct PersistentAlbumView: View {
@Environment(\.modelContext) private var dbContext
@Query private var mediaItems: [Media]
let album: Album
//...
}