为什么数据不填充 SwiftUI 中弹出框内的列表?

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

我在寻找什么

我希望有人可以帮助我了解正在发生的事情以及为什么 SwiftUI 在这种情况下会表现得如此。

我想做什么

我最初尝试创建一个带有选择器的菜单,其中有一个日历列表供用户选择。对我来说重要的是显示一个彩色圆圈来指示日历的颜色以及标题。当时,我不知道如何让选择器菜单中的图标着色。所以,我认为带有列表的自定义弹出窗口会起作用。这就是让我发现这个问题的原因。

设置

  • 我有一个类正在为我管理异步创建或检索数据。
    • 我正在使用该数据来填充选择器菜单和/或弹出框列表的列表
  • 我有一个
    State
    属性,可以在获取数据后保存视图中的数据
  • 该数据是在
    .task
    修饰符内部获取的(也尝试在
    Task
    内使用
    onAppear
  • 最后,我有一个由
    VStack
    组成的视图,其中包含触发弹出窗口的按钮以及充当菜单的选择器。两者都填充了相同的数据

问题

当选择器被注释掉时,弹出窗口始终显示为空,但它保留了一个框架。当选择器取消注释时,数据不仅会显示在选择器中,还会显示在弹出窗口中。这就是我真正难住的地方。为什么只有当选择器也在那里时才会填充弹出窗口中的列表?

我尝试过的事情

  • 将弹出窗口视图放入其自己的文件中
  • 使用
    onAppear
    代替
    .task
  • 在弹出窗口内使用
    VStack
    代替
    List
  • 将获取的结果作为可选选项放在数据管理器类中,然后在数据获取完成时随时重置

代码

可以轻松复制并亲自尝试

import SwiftUI

struct TestView: View {
    let dataManager = DataManager()
    @State private var items: [DataItem] = []
    @State private var popoverIsPresented = false
    
    var body: some View {
        VStack {
            Button {
                popoverIsPresented = true
            } label: {
                Text("Choose Item")
            }
            .popover(isPresented: $popoverIsPresented, content: {
                List {
                    ForEach(items) { item in
                        Text(item.title)
                    }
                }
            })
            
//          List {
//              ForEach(items) { item in
//                  Text(item.title)
//              }
//          }
        }
        .task { items = await dataManager.fetchData() }
    }
}

#Preview {
    TestView()
        .frame(minWidth: 300, minHeight: 300)
}

struct DataItem: Identifiable {
    let id = UUID()
    let title: String
}

class DataManager {
    
    func fetchData() async -> [DataItem] {
        Array(repeating: .init(title: "This is an example item"), count: 10)
    }
}
  1. 按原样运行
  2. 然后尝试取消注释选择器,然后您应该会看到数据出现在两个视图中

最佳猜测🤷u200d♂️

  • 一旦
    State
    属性更改,弹出窗口就不会更新(我认为这将是一个错误)

图片

With Picker commented out

With Picker left in

swiftui popover swiftui-picker
1个回答
0
投票

如果我理解正确的话,当您引用 picker 时,您实际上指的是示例中的

List
,对吗?

我能够通过取消注释

List
来重现该问题。但是,控制台中出现错误:

ForEach:ID 1582CFA9-24C2-49BE-93DF-1D8B5FB949EA 在集合中多次出现,这将给出未定义的结果!

要解决此问题,需要以不同的方式构建虚拟数组。例如:

func fetchData() async -> [DataItem] {
//    Array(repeating: .init(title: "This is an example item"), count: 10)
    var result = [DataItem]()
    for n in 0..<10 {
        result.append(DataItem(title: "This is an example item \(n)"))
    }
    return result
}

List
被注释掉时,
Popover
保持为空,正如您所描述的。我的猜测是,当将数组分配给状态变量时,视图的
body
不会被刷新,因为没有任何可见内容依赖于状态变量。正如您所建议的,这可能确实是一个错误。

作为解决方法,请尝试以某种方式使

body
依赖于状态变量。例如,您可以将项目计数显示为
Text
后面隐藏的
Button
元素:

Button {
    popoverIsPresented = true
} label: {
    Text("Choose Item")
}
.background { Text("\(items.count)").hidden() } // 👈 ADDED
.popover(isPresented: $popoverIsPresented) {
    List {
        ForEach(items) { item in
            Text(item.title)
        }
    }
}

关于原来的要求:

对我来说重要的是显示一个彩色圆圈来指示日历的颜色以及标题。

👉 使用 Rectangle() 而不是 Text() 的 SwiftUI Picker 问题的答案展示了如何做到这一点。

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