使用 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?
我想将 .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 时不会执行,因此不会执行你到一个新的视图。
如果我将相同的导航 Link 向下移动到 .swipeActions 之后,并使用一些 @State 调用它,它会起作用,但它会在 ForEach 列表的每一行之间添加另一行。换句话说,ForEach 当然将其视为其列表的一部分,并将其与列表中的其他项目一起添加。即使我在 NavigationLink 上添加 .hidden(),它仍然会占用 space 新行,它只是隐藏行的内容,而不是行本身。
如果我将 NavigationLink 移到 ForEach 之外,则 ForEach 的颜色输入参数超出范围。它将正确构建视图并执行 link(使用一个动作和一些 @State),但它不能传递来自 ForEach 的颜色输入,因为它当然超出了范围。如果您在其位置硬编码一种颜色,它可以正常工作,当然除了它没有用户从列表中选择的颜色这一事实。
请注意,我还在视图底部放置了一个简单的导航Link,这样我就可以看到它在 .swipeActions 问题之外正常工作,并且它在硬编码颜色值,如 Color.red.
这当然是一个虚构的例子,但我认为它确实说明了问题。
是否有人使用 .swipeActions 调用导航Link 到新视图,将用户选择的项目(在本例中为颜色)传递到该视图?如果是这样,你如何让它发挥作用。感觉就像先有鸡还是先有蛋的问题,我似乎无法同时访问输入数据(颜色)可用的范围,以及未成为视图一部分的 NavigationLink ForEach 列表。
有人有什么想法吗?预先感谢任何和所有评论、更正、想法等。
第一个解决方案:使用 fullScreenCover 和 @State var selectedColor
@Environment(.presentationMode) var presentationMode
// ContentView.swift
// Whosebug
//
// 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
// Whosebug
//
// 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)
}
}
}
我想将 .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 时不会执行,因此不会执行你到一个新的视图。
如果我将相同的导航 Link 向下移动到 .swipeActions 之后,并使用一些 @State 调用它,它会起作用,但它会在 ForEach 列表的每一行之间添加另一行。换句话说,ForEach 当然将其视为其列表的一部分,并将其与列表中的其他项目一起添加。即使我在 NavigationLink 上添加 .hidden(),它仍然会占用 space 新行,它只是隐藏行的内容,而不是行本身。
如果我将 NavigationLink 移到 ForEach 之外,则 ForEach 的颜色输入参数超出范围。它将正确构建视图并执行 link(使用一个动作和一些 @State),但它不能传递来自 ForEach 的颜色输入,因为它当然超出了范围。如果您在其位置硬编码一种颜色,它可以正常工作,当然除了它没有用户从列表中选择的颜色这一事实。
请注意,我还在视图底部放置了一个简单的导航Link,这样我就可以看到它在 .swipeActions 问题之外正常工作,并且它在硬编码颜色值,如 Color.red.
这当然是一个虚构的例子,但我认为它确实说明了问题。
是否有人使用 .swipeActions 调用导航Link 到新视图,将用户选择的项目(在本例中为颜色)传递到该视图?如果是这样,你如何让它发挥作用。感觉就像先有鸡还是先有蛋的问题,我似乎无法同时访问输入数据(颜色)可用的范围,以及未成为视图一部分的 NavigationLink ForEach 列表。
有人有什么想法吗?预先感谢任何和所有评论、更正、想法等。
第一个解决方案:使用 fullScreenCover 和 @State var selectedColor @Environment(.presentationMode) var presentationMode
// ContentView.swift
// Whosebug
//
// 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
// Whosebug
//
// 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)
}
}
}