在子视图中添加@Query宏时,子视图未渲染,为什么?

问题描述 投票:0回答:1

由于某种原因,当我向子视图添加

@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。预先感谢!

swiftui cloudkit swiftdata
1个回答
0
投票

我花了一个多星期的时间到处修补这个问题,因为我有时间,奇怪的是,我在发布问题后能够解决这个问题。我没有删除问题,而是想分享解决方案和问题,以帮助其他人在多个子视图中使用 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

    //...
}
© www.soinside.com 2019 - 2024. All rights reserved.