【问题标题】:SwiftUI Thread 1: Fatal error: each layout item may only occur onceSwiftUI Thread 1:致命错误:每个布局项只能出现一次
【发布时间】:2020-12-16 17:16:51
【问题描述】:

这个小代码显示来自具有 2 列的 API 的文章,scrollview 位于 ArticleView。不幸的是,在第 6 个 loadMoreContentIfNeeded 之后,这个错误让我很难受

线程 1:致命错误:每个布局项只能出现一次

我尝试在我的代码中添加.id(UUID()) 以避免出现 id 问题,但它也没有帮助。我还观察到这个错误也发生在 LazyVStavk 中。如果您将 LazyVStack 更改为 VStack,则不会出现此问题。但是 SwiftUI 中没有任何东西叫做 VGrid

struct ArticleView: View {
@ObservedObject var service = ArticleService()
let columns: [GridItem] = Array(repeating: GridItem(.flexible(), spacing: 0), count: 2)
var body: some View {     
ScrollView(.vertical, showsIndicators: false) {
                        LazyVGrid(columns: columns, spacing:0) {
                            ForEach(Array(service.articlePost.enumerated()), id: \.1.article_id) { i, post in
                                ArticleListElementView(articlePost: post, cellNumber: i)
                                    .id(UUID())
                                    .onAppear {
                                        self.service.loadMoreContentIfNeeded(currentItem: post)
                                    }
                                    .padding(.all, 20)
                                    .onTapGesture {
                                        self.shown = true
                                        self.selectedArticle = post
                                    }
                               }
                          }
                     }

 private func loadMoreContent() {
    
    guard !isLoadingPage && canLoadMorePages else {
        return
    }
    isLoadingPage = true
    
    let parameters = ["index": "articles", "start" : "\(currentPage)"]
    if let url = URL(string: "http:/v2/search/") {
        let session = URLSession(configuration: .default)
        var request = URLRequest(url:url)
        request.httpMethod = "POST"
        
        do {
            request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted)
        } catch let error {
            print(error.localizedDescription)
        }
        
        request.addValue("application/json", forHTTPHeaderField: "Content-Type")
        request.addValue("application/json", forHTTPHeaderField: "Accept")
        
        session.dataTaskPublisher(for: request as URLRequest)
            .map(\.data)
            .decode(type: ArticleResults.self, decoder: JSONDecoder())
            .receive(on: DispatchQueue.main)
            .handleEvents(receiveOutput: { response in
                self.isLoadingPage = false
                self.currentPage += 1
            })
            .map({ response in
                return self.articlePost + response.articles
            })
            .catch({ _ in Just(self.articlePost) })
            .assign(to: &$articlePost)
    }
    
}

func loadMoreContentIfNeeded(currentItem item: ArticlePost?) {
    guard let item = item else {
        loadMoreContent()
        return
    }
    
    let thresholdIndex = articlePost.index(articlePost.endIndex, offsetBy: -5)
    if articlePost.firstIndex(where: { $0.id == item.id }) == thresholdIndex {
        loadMoreContent()
    }
}

【问题讨论】:

    标签: swiftui lazyvgrid


    【解决方案1】:

    我只能指出问题所在,因为仍然缺少一些相关代码。

    问题在于您的 ForEach 或您的数组。您正在通过id: \.1.article_id 识别内容。不过,我猜!在您的数组中有具有相同文章 ID 的项目。 LazyVGrid 要求您的内容具有明确唯一的 id,而 VStack 则没有。

    由于我不知道您的 Array 或 loadMoreContentIfNeeded 函数,因此我无法指出您的问题,为什么会有重复项。

    这里有一个例子来告诉你区别:

    这会崩溃,因为服务包含两次“Test123”并且您通过它们的内容来识别它

    struct ContentView: View {
        var service = ["Test123", "Test1235", "Test123", "dasdkjasdkas"]
        let columns: [GridItem] = Array(repeating: GridItem(.flexible(), spacing: 0), count: 2)
        
        var body: some View {
            ScrollView(.vertical, showsIndicators: false) {
                LazyVGrid(columns: columns, spacing:0) {
                    ForEach(service, id: \.self) { element in
                        Text(element)
                            .id(UUID())
                            .onAppear {
                                //self.service.loadMoreContentIfNeeded(currentItem: post)
                            }
                            .padding(.all, 20)
                            .onTapGesture {
                                //self.shown = true
                                //self.selectedArticle = post
                            }
                    }
                }
            }
        }
    }
    

    删除一个“Test123”,它就可以正常工作了。

    【讨论】:

    • 我需要我的 .enumarated() 部分,因为我正在根据单元格编号修改我的 ArticleListElement。所以删除它不是一种选择。
    • 是的,你可以保留它。确保您没有重复的项目。
    • 好的,但是如果我让它 test123,我正在使用 articleService() 获取数据,也许它会起作用,但它会没用
    • 答案让我走上了正确的道路。我只是将var id = UUID()添加到我的可识别结构并删除了我以前的id创建方法。现在可以了,谢谢。
    猜你喜欢
    • 2020-12-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-23
    相关资源
    最近更新 更多