绑定两个 ForEach 循环以更新每个项目单元格

Posted

技术标签:

【中文标题】绑定两个 ForEach 循环以更新每个项目单元格【英文标题】:Binding two ForEach loop to update each item cell 【发布时间】:2021-09-24 01:58:50 【问题描述】:

这是我的第二篇文章,我尽可能地需要你的帮助。我正在我的父视图和详细视图上创建一个收藏按钮。我需要两个按钮才能相互对应。当我在父视图的 ForEach 循环上标记为收藏时,我想在我的详细视图中显示该项目是收藏​​的。此外,我可以从我的详细视图副 vasa 中取消收藏或收藏。我真的很难弄清楚如何绑定这两个 ForEach 循环。下面我提供了一个我的代码示例。如果你想用我的完整代码进行测试,你可以在这里访问它:Making favorite button from several layers and binding two list using EnvironmentObject

struct Data: Identifiable 

    let id = UUID()
    let number: Int
    var name1: String
    let name2: String


public struct DataList 
    
    static var dot = [
        
        Data(number: 1,
              
            name1: "Pasian Phatna",
            
            name2: "Praise God, from whom All Blessings Flow"),

        Data(number: 2,
              
            name1: "Itna Kumpi, Ka Tuu-Cing Pa",
            name2: "The King of Love My Shephaerd Is (Dominus Regit Me)"),
            
        Data(number: 3,
              
            name1: "Kumpipa Bia Un",
            
            name2: "O Worship the King"),
            
        Data(number: 4,
          
            name1: "Pa Tung Min Than'na Om Hen",
            name2: "Gloria Patri (1st Tune)"),
            
        Data(number: 5,
        
            name1: "Pa Tung Min Than'na Om Hen",
            
            name2: "Gloria Patri (2nd Tune)")       
    ]


struct ParentView: View 
    
    @State var datas: [Data] = DataList.dot
    
    var body: some View 
        
        NavigationView 
            
            ScrollView (.vertical, showsIndicators: false) 
                LazyVStack(spacing: 5)  

                    ForEach (datas, id: \.id)  data in

                        MainData(data: data)
                        
                        
                        Divider()
                            .padding(.all)
                    
                
            
            .navigationBarHidden(true)
        
        .navigationViewStyle(StackNavigationViewStyle())
    


struct MainData: View 

    @State var data: Data
    @State var selectedFavoriteSong: Bool = false
    
    
    var body: some View 
        HStack 
            Button(action: 
                self.selectedFavoriteSong.toggle()
            , label: 
                if selectedFavoriteSong 
                    Image(systemName: "suit.heart.fill")
                        .foregroundColor(.red)
                        .padding(.horizontal)

                 else 
                    Image(systemName: "suit.heart")
                        .padding(.horizontal)
                
            )
            
            Spacer()
            Text("\(data.number)")

            Spacer()
            
        
        .padding(.top)
        
        VStack 
            
            Text(data.name1)
                .font(.title2.smallCaps())
                .fontWeight(.bold)
                .foregroundColor(.primary)
            
            Text(data.name2)
                .font(.title3)
                .fontWeight(.medium)
                .foregroundColor(.secondary)
                .italic()
        
        .padding(.horizontal)
        .multilineTextAlignment(.center)
    

请注意,当我点击搜索图标(此处未显示)时,会弹出下面的 Search()。我的观点是 Search() 不是直接连接到 ParentView() 而是 DetailView() 嵌入在 Search() 中。

struct Search: View 

    @State var datas: [Data] = DataList.dot
    
    var body: some View 
        NavigationView 
            ScrollView (.vertical, showsIndicators: false) 
                LazyVStack(alignment: .leading, spacing: 10) 
                    
                    ForEach (datas, id: \.id)  data in
                        
                        NavigationLink(
                            destination: DetailView(data: data),
                            label: 
                                Text("Search")
                            )

                        
                    
                    .padding(.horizontal)
                
        
    


struct DetailView: View 
    
    @State var data: Data
    @State var selectedFavoriteSong: Bool = false
    
    var body: some View 
        HStack 
            Button(action: 
                self.selectedFavoriteSong.toggle()
            , label: 
                if selectedFavoriteSong 
                    Image(systemName: "suit.heart.fill")
                        .foregroundColor(.red)
                        .padding(.horizontal)

                 else 
                    Image(systemName: "suit.heart")
                        .padding(.horizontal)
                
            )
            
            Spacer()
            Text("\(data.name1)")
            Spacer()

        
        .padding(.top)
        
        VStack 
            
            Text(data.name2)
                .font(.title2.smallCaps())
                .fontWeight(.bold)
                .foregroundColor(.primary)
        
        .padding(.horizontal)
        .multilineTextAlignment(.center)

        Spacer()
        
    

所以,我想用某种绑定属性连接父视图和详细视图。但这两者是不可能联系起来的。我可以存储

@State var selectedFavoriteSong: Bool = false

在环境对象内。但是当我点击收藏时,ForEach 循环内的所有项目都被选中。请在这个问题上帮助我。如果您需要完整的代码,上面的链接将指向我的第一篇文章。谢谢。

【问题讨论】:

什么是DataList?它没有在此处或您的其他链接中定义。请尝试在此问题中包含minimal reproducible example,可以将其复制并粘贴到 Xcode 中进行测试。 我将重新发布 DataList 的示例。 【参考方案1】:

我建议将所有数据存储在父视图拥有的 ObservableObject 中,然后可以传递到子视图(显式地或通过EnvironmentObject):

class DataSource : ObservableObject 
    @Published var data : [Data] = DataList.dot
    @Published var favoritedItems: Set<UUID> = []
    
    func favoriteBinding(forID id: UUID) -> Binding<Bool> 
        .init 
            self.favoritedItems.contains(id)
         set:  newValue in
            if newValue 
                self.favoritedItems.insert(id)
             else 
                self.favoritedItems.remove(id)
            
        
    

例如:

struct ParentView : View 
    @StateObject var dataSource = DataSource()
    
    var body: some View 
        VStack 
            Search(dataSource: dataSource)
        
    

请注意,数据源存储已收藏的 ID 列表。它使用自定义绑定,可以将布尔值向下传递到详细视图:


struct Search: View 

    @ObservedObject var dataSource : DataSource
    
    var body: some View 
        NavigationView 
            ScrollView (.vertical, showsIndicators: false) 
                LazyVStack(alignment: .leading, spacing: 10) 
                    
                    ForEach (dataSource.data, id: \.id)  data in
                        
                        NavigationLink(
                            destination: DetailView(data: data,
                                                    selectedFavoriteSong: dataSource.favoriteBinding(forID: data.id)),
                            label: 
                                Text(data.name1)
                            )

                        
                    
                    .padding(.horizontal)
                
        
    


struct DetailView: View 
    var data : Data
    @Binding var selectedFavoriteSong : Bool
    
    var body: some View 
        HStack 
            Button(action: 
                self.selectedFavoriteSong.toggle()
            , label: 
                if self.selectedFavoriteSong 
                    Image(systemName: "suit.heart.fill")
                        .foregroundColor(.red)
                        .padding(.horizontal)

                 else 
                    Image(systemName: "suit.heart")
                        .padding(.horizontal)
                
            )
            
            Spacer()
            Text("\(data.name1)")
            Spacer()

        
        .padding(.top)
        
        VStack 
            
            Text(data.name2 ?? "")
                .font(.title2.smallCaps())
                .fontWeight(.bold)
                .foregroundColor(.primary)
        
        .padding(.horizontal)
        .multilineTextAlignment(.center)

        Spacer()
        
    

【讨论】:

原来我的 MainData() 不见了。我必须删除那个吗? 没有。您可以对它应用与我在上面展示的完全相同的原则。只需确保您将数据和绑定传递给它。 每次我返回 ParentView 时,我的应用程序都会崩溃,绑定还没有起作用。但是当你指导我的方法时,我明白了。我认为我的代码太乱了,我需要正确应用您的指导。非常感谢@jnpdx,因为当我需要帮助时,你总是在那里。此外,课堂上的功能对我来说是全新的。我必须研究那个方法。再次感谢您。 @Lian Hmm - 崩溃听起来不太好。但是,是的,一旦你弄清楚如何实现它,使用这种模式可能会帮助你在视图之间有效地共享数据。知道如何编写这些自定义绑定也非常有用。 我找到了崩溃的错误。问题是我必须将 EnvironmentObject 传递给 NavigationView。原来这是 ios 14 的问题。基本上,您提供的答案是有效的。非常感谢。

以上是关于绑定两个 ForEach 循环以更新每个项目单元格的主要内容,如果未能解决你的问题,请参考以下文章

将数据从枚举传递到在ForEach循环中触发的sheet中。

在保存事件之前,需要帮助以同时循环两个单元格

如何获取与表连接绑定的 Row datagridview selectedItem 的每个单元格的值?

NSTableView,多个单元格和绑定

php foreach 循环中的 HTML 表格,其中仅当元素等于表格列标题名称时才会填充单元格数据

基于另一个单元格(组合框)更新GridPanel单元格(组合框),两个单元格属于同一列?