使用 ForEach 中的 SwiftUI 3.0 .swipeActions,您如何在将 ForEach 的输入参数传递给该视图时让一个动作转到另一个视图?

Posted

技术标签:

【中文标题】使用 ForEach 中的 SwiftUI 3.0 .swipeActions,您如何在将 ForEach 的输入参数传递给该视图时让一个动作转到另一个视图?【英文标题】:With SwiftUI 3.0 .swipeActions in a ForEach how do you have an action go to another view while passing that view the input argument of the ForEach? 【发布时间】:2021-12-31 19:44:30 【问题描述】:

我想将 .swipeAction 添加到 ForEach 列表中,我想在其中将用户选择的列表中的元素传递给另一个调用的视图。换句话说,当用户在列表中的某个项目上滑动时,我希望用户被带到一个新视图,该视图具有列表中该项目的内容,以便它可以在该新视图中显示该项目的详细信息。

话虽如此,我已经模拟了这个简单的示例,希望它有助于说明我遇到的问题。

import SwiftUI

struct ContentView: View 
  
  var colors : [Color] = [Color.red, Color.green,
                          Color.blue, Color.yellow,
                          Color.brown, Color.cyan, Color.pink]
  
  var body: some View 
    NavigationView 
      VStack 
        List 
          ForEach(colors, id: \.self)  color in
            ColorRowView(color: color)
              .swipeActions(edge: .trailing, allowsFullSwipe: false) 
                Button(action:  print("Hello From The First Button") ,
                       label: Label("Hello", systemImage: "face.smiling"))
                .tint(Color.orange)
                NavigationLink(destination: ColorShowView(color: color),
                               label: Image(systemName: "magnifyingglass"))
               .tint(.yellow)
              
          
        
        Spacer()
        NavigationLink(destination: ColorShowView(color: Color.red),
                       label:  Image(systemName: "magnifyingglass" )  ).tint(.yellow)
      
      .navigationViewStyle(StackNavigationViewStyle())
      .navigationTitle(Text("List Of Colors"))
      .navigationBarTitleDisplayMode(.inline)
    
  
  


struct ColorRowView: View 
  
  var color: Color
  
  var body: some View 
    VStack 
      Text("Color \(color.description)").foregroundColor(color)
    
  
  


struct ColorShowView: View 
  
  var color: Color
  
  var body: some View 
    VStack 
      Text("Color Name \(color.description)").foregroundColor(color)
      Text("Color Hash Value \(color.hashValue)").foregroundColor(color)
    
  
  

我发现,如果我将 NavigationLink 作为按钮放在 .swipeActions 上,它会正确显示,但点击时 NavigationLink 不会执行,因此不会将您带到新视图。

如果我将同一个导航链接向下移动到 .swipeActions 之后,并使用一些 @State 调用它,它可以工作,但它会在 ForEach 列表的每一行之间添加另一行。换句话说,ForEach 当然将其视为其列表的一部分,并将其与列表中的其他项目一起添加。即使我在 NavigationLink 上添加了 .hidden(),它仍然会占用新行的空间,它只是隐藏了行的内容,而不是行本身。

如果我将 NavigationLink 移到 ForEach 之外,则 ForEach 中颜色的输入参数超出范围。它将正确构建视图并执行链接(使用操作和一些@State),但它不能传递来自 ForEach 的颜色输入,因为它当然超出了范围。如果您在其位置硬编码颜色,则它可以正常工作,当然除了它没有用户从列表中选择的颜色这一事实。

请注意,我还在视图底部放置了一个简单的 NavigationLink,以便我可以看到它在 .swipeActions 问题之外正常工作,并且它在使用硬编码颜色值(如 Color)时也能正常工作。红色。

这当然是一个非常虚构的例子,但我认为它确实说明了问题。

有没有人使用 .swipeActions 来调用 NavigationLink 到将用户选择的项目(在本例中为颜色)传递到该视图的新视图?如果是这样,你如何让它发挥作用。感觉就像先有鸡还是先有蛋的问题,我似乎无法同时访问输入数据(颜色)可用的范围,以及未成为 ForEach 列表视图一部分的 NavigationLink。

有人有什么想法吗?提前感谢您的任何和所有评论、更正、想法等。

【问题讨论】:

【参考方案1】:

第一个解决方案:使用 fullScreenCover 和 @State var selectedColor @Environment(.presentationMode) varpresentationMode

//  ContentView.swift
        //  ***
        //
        //  Created by Mustafa T Mohammed on 12/31/21.
        //
    
    import SwiftUI
    
    struct ContentView: View 
        @State var isColorShowViewPresented = false
        @State var selectedColor: Color = .yellow
        var colors : [Color] = [Color.red, Color.green,
                                Color.blue, Color.yellow,
Color.brown, Color.cyan, Color.pink]
    
        var body: some View 
            NavigationView 
                VStack 
                        // you can use List instead of ForEach loop it's the same
                        // less code :)
                    List(colors, id: \.self)  color in
                        ColorRowView(color: color)
                            .swipeActions(edge: .trailing, allowsFullSwipe: false) 
                                Button(action: 
                                    isColorShowViewPresented.toggle() // toggle isColorShowViewPresented to trigger the
                                                                                                     // fullScreenCover
                                    selectedColor = color
                                    print("Hello From The First Button")
                                ,
                                             label: Label("Hello", systemImage: "face.smiling"))
                                    .tint(Color.orange)
                            
    
                    
                    Spacer()
                        .fullScreenCover(isPresented: $isColorRowViewPresented) 
                                // if you like to implement what happen when user dismiss the presented view
                            print("user dissmissed ColorRowView")
                         content: 
                            ColorShowView(color: selectedColor) // view that you want to present
                        
                
                .navigationViewStyle(StackNavigationViewStyle())
                .navigationTitle(Text("List Of Colors"))
                .navigationBarTitleDisplayMode(.inline)
            
        
    
    
    
    struct ColorRowView: View 
    
        var color: Color
        var body: some View 
            VStack 
                Text("Color \(color.description)").foregroundColor(color)
            
        
    
    
    
    struct ColorShowView: View 
        @Environment(\.presentationMode) var presentationMode
    
        var color: Color
    
        var body: some View 
            VStack 
                Button 
                    presentationMode.wrappedValue.dismiss()
                 label: 
                    Text("Dismiss")
                
                Spacer()
                Text("Color Name \(color.description)").foregroundColor(color)
                Text("Color Hash Value \(color.hashValue)").foregroundColor(color)
                Spacer()
            
        
    
    

第二种解决方案:NavigationLink 和 @State var selectedColor

    //
    //  ContentView.swift
    //  ***
    //
    //  Created by Mustafa T Mohammed on 12/31/21.
    //

import SwiftUI

struct ContentView: View 
    @State var isColorShowViewPresented = false
    @State var selectedColor: Color = .yellow
    var colors : [Color] = [Color.red, Color.green,
                                                    Color.blue, Color.yellow,
                                                    Color.brown, Color.cyan, Color.pink]

    var body: some View 
        NavigationView 
            VStack 
                    // you can use List instead of ForEach loop it's the same
                    // less code :)
                List(colors, id: \.self)  color in
                    ColorRowView(color: color)
                        .swipeActions(edge: .trailing, allowsFullSwipe: false) 
                            Button(action: 
                                isColorShowViewPresented.toggle() // toggle isColorShowViewPresented to trigger the
                                                                                                 // NavigationLink
                                selectedColor = color
                                print("Hello From The First Button")
                            ,
                                         label: Label("Hello", systemImage: "face.smiling"))
                                .tint(Color.orange)
                        

                
                Spacer()
                NavigationLink("", isActive: $isColorShowViewPresented) 
                    ColorShowView(color: selectedColor)
                
            
            .navigationViewStyle(StackNavigationViewStyle())
            .navigationTitle(Text("List Of Colors"))
            .navigationBarTitleDisplayMode(.inline)
        
    



struct ColorRowView: View 

    var color: Color
    var body: some View 
        VStack 
            Text("Color \(color.description)").foregroundColor(color)
        
    



struct ColorShowView: View 
    var color: Color

    var body: some View 
        VStack 
            Text("Color Name \(color.description)").foregroundColor(color)
            Text("Color Hash Value \(color.hashValue)").foregroundColor(color)
        
    


【讨论】:

这两种解决方案都有效。谢谢穆斯塔法。我将使用 NavigationLink 解决方案以与我的应用程序的其余部分保持一致,但我什至没有考虑将 .fullScreenCover 作为可能的解决方案路径。谢谢你让我重回正轨。新年快乐。

以上是关于使用 ForEach 中的 SwiftUI 3.0 .swipeActions,您如何在将 ForEach 的输入参数传递给该视图时让一个动作转到另一个视图?的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI 中的 @Binding 和 ForEach

在 SwiftUI 中的 ForEach 中绑定

为啥我不能访问这个数组 ForEach 循环中的数据? SwiftUI

SwiftUI 中的两个 ForEach

SwiftUI 使用 List 和 Foreach 遍历字典中的键并创建列表视图

SwiftUI:删除 ForEach 中的最后一行