ios 无法使SwiftUI列表选择与自定义结构一起使用

lg40wkob  于 2023-01-03  发布在  iOS
关注(0)|答案(2)|浏览(159)

我正在尝试做一个最喜欢的报纸列表。在编辑模式下,列表显示所有可用的报纸,用户可以从中选择他的最爱。选择最爱后,列表只显示最爱。下面是我的代码:

struct Newspaper: Hashable {
     let name: String
 }
 
 struct ContentView: View {
     @State var editMode: EditMode = .inactive
     @State private var selection = Set<Newspaper>()
     var favorites: [Newspaper] {
         selection.sorted(by: ({ $0.name < $1.name }))
     }
     
     let newspapers = [
         Newspaper(name: "New York Times"),
         Newspaper(name: "Washington Post")
     ]
     
     var body: some View {
         NavigationView {
             List(editMode == .inactive ? favorites : newspapers, id: \.name, selection: $selection) { aliasItem in
                 Text(aliasItem.name)
             }
             .toolbar {
                 EditButton()
             }
             .environment(\.editMode, self.$editMode)
         }
     }
 }

问题是列表进入编辑模式,但是选择小部件没有出现。如果我用String数组替换Newspaper(并相应地修改其余代码),那么选择小部件确实出现了,并且列表按预期工作。有人能解释一下问题是什么吗?
我最初尝试使用Identifiable报纸,如下所示:

struct Newspaper: Codable, Identifiable, Equatable, Hashable {
     var id: String { alias + publicationName }
     let alias: String
     let publicationName: String
 }

由于这不起作用,我测试了上面的简单版本,试图找出问题所在。
因为我需要保存收藏夹,所以Newspapers必须是Codable,因此不能使用UUID,因为它们是从磁盘读取的,而完整的Newspapers数组是从服务器获取的,这就是为什么我将id作为计算属性。
Yrb:s的回答提供了问题的解决方案:选择集的类型必须与Identifiable结构体中使用的id类型相同,而不是List中显示的类型。
因此,在我的例子中(使用Identifiable Newspaper版本),选择集必须是Set<String>类型,而不是Set<Newspaper>类型,因为Newspaper的idString

eeq64g8w

eeq64g8w1#

您的问题源于List's选择模式使用id:属性来跟踪您的选择。由于您将List声明为List(..., id: \.name, ...),因此selection变量的类型需要为String。如果将其更改为List(..., id: \.self, ...),它将正常工作。但是在这样的列表中使用self会带来它自己的问题。为了保持最佳实践,暂时忘记选择,您应该使用Identifiable结构。List应该通过结构上的id参数来标识元素。(我使用了UUID)
对于选择,这意味着您需要将其定义为@State private var selection = Set<UUID>()。这就留下了处理favorites计算变量的工作。您只需过滤newspapers数组以查找包含在selection中的元素,而不是返回selection的数组。最后,这就留下了以下内容:

struct Newspaper: Identifiable, Comparable {
    let id = UUID()
    let name: String
    
    static func < (lhs: Newspaper, rhs: Newspaper) -> Bool {
        lhs.name < rhs.name
    }
}

 struct ContentView: View {
    @State var editMode: EditMode = .inactive
    @State private var selection = Set<UUID>()
    var favorites: [Newspaper] {
        newspapers.filter { selection.contains($0.id) }
    }
    
    let newspapers = [
        Newspaper(name: "New York Times"),
        Newspaper(name: "Washington Post")
    ]
    
    var body: some View {
        NavigationView {
            VStack {
                List(editMode == .inactive ? favorites.sorted() : newspapers, selection: $selection) { aliasItem in
                    Text(aliasItem.name)
                }
                .toolbar {
                    EditButton()
                }
                .environment(\.editMode, self.$editMode)
                Text(selection.count.description)
            }
        }
    }
}

编辑:
在您的评论中,您说Newspaper需要"可编码",并暗示服务器响应中没有id参数。下面的NewspaperCodable,但不希望服务器响应中有id,而是简单地加上它自己的常量id。计算id是一个非常糟糕的主意。id应该永远不会改变,并且应该是唯一的。UUID可以提供这些。

struct Newspaper: Identifiable, Comparable, Codable {
    let id = UUID()
    let name: String
    
    enum CodingKeys:String,CodingKey {
        case name
    }

    static func < (lhs: Newspaper, rhs: Newspaper) -> Bool {
        lhs.name < rhs.name
    }
}
sdnqo3pr

sdnqo3pr2#

您可以将结构体用于List的选择,但必须告知SwiftUI这一点,因此它不使用结构体的ID。原始代码使用添加的适当标记,特别是:

Text(aliasItem.name)
    .tag(aliasItem)

相关问题