描述
我有以下代码:
NavigationView {
List {
ForEach(someList) { someElement in
NavigationLink(destination: someView()) {
someRowDisplayView()
} //: NavigationLink
} //: ForEach
} //: List
} //: NavigationView
基本上,它显示先前由用户输入填充的类对象的动态列表。我有一个“addToList()”函数,它允许用户添加一些元素(比如说姓名、电子邮件等),一旦用户确认它将元素添加到这个列表中。然后我有一个视图,它通过ForEach显示列表,然后为每个元素创建一个NavigationLink,如上面的代码示例所示。
一旦点击了这个列表的一个元素,用户应该被正确地重定向到一个目标视图,正如NavigationLink所暗示的那样。所有这一切都很好。
我想要的是
这是我面临一个问题的地方:我希望用户能够编辑此列表中一行的内容,而不必删除该行并重新添加另一行。
我决定使用 SwiftUI 的EditMode()功能。所以这就是我想出的:
@State private var editMode = EditMode.inactive
[...]
NavigationView {
List {
ForEach(someList) { someElement in
NavigationLink(destination: someView()) {
someRowDisplayView()
} //: NavigationLink
} //: ForEach
} //: List
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
EditButton()
}
}
.environment(\.editMode, $editMode)
} //: NavigationView
当我单击“编辑”按钮时,会正确触发EditMode 。但我注意到列表行在编辑模式下不可点击,这很好,因为我不希望它在编辑模式下跟随NavigationLink 。
虽然我想要的是将用户重定向到允许编辑点击行的视图,或者更好的是,将编辑表呈现给用户。
我试过的
由于我无法在编辑模式下点击该行,因此我尝试了几种技巧,但都没有按照我的意愿得出结论。这是我的尝试。
.simultaneousGesture
我尝试将修改后的.simultaneousGesture添加到我的NavigationLink并切换@State变量以显示版本表。
@State private var isShowingEdit: Bool = false
[...]
.simultaneousGesture(TapGesture().onEnded{
isShowingEdit = true
})
.sheet(isPresented: $isShowingEdit) {
EditionView()
}
这根本行不通。似乎这个.simultaneousGesture以某种方式破坏了 NavigationLink,点击成功就像五次中的一次。
即使在编辑模式上添加条件也不起作用,例如:
if (editMode == .active) {
isShowingEdit = true
}
在非版本模式下,NavigationLink 仍然存在漏洞。但我注意到,一旦进入编辑模式,它就会像我想要的那样做。
View 上的修饰符条件扩展
在上一次失败之后,我的第一个想法是我需要仅在编辑模式下触发点击手势,因此在非编辑模式下,视图甚至不知道它有点击手势。
我决定向View 结构添加一个扩展,以便为修饰符定义条件(我在 Stackoverflow 的某处找到了此代码示例):
extension View {
@ViewBuilder func `if`<Content: View>(_ condition: Bool, transform: (Self) -> Content) -> some View {
if condition {
transform(self)
} else {
self
}
}
}
然后在我的主代码中,而不是之前尝试过的 .simultaneousGesture 修饰符,我将其添加到NavigationLink:
.if(editMode == .active) { view in
view.onTapGesture {
isShowingEdit = true
}
}
它有效,一旦用户在编辑模式下点击一行,我就能够显示此版本视图,并且正常的非编辑模式仍然有效,因为它正确触发了 NavigationLink。
剩下的问题
但是有些事情让我感到困扰,它感觉不像是自然或原生的行为。一旦被点击,该行没有显示任何反馈,就像它在跟随NavigationLink时在非编辑模式下显示的那样:即使在很短的时间内它也不会突出显示。
点击手势修改器只是执行我所询问的代码,没有动画、反馈或任何东西。
我希望用户知道并查看点击了哪一行,并且我希望它像点击 NavigationLink 时一样突出显示:只需让它看起来就像该行实际上是 "点击"。我希望这是有道理的。
我还希望在用户点击整行时触发代码,而不仅仅是文本可见的部分。目前,点击该行的空白字段不会执行任何操作。它必须有文字。
一个更好的解决方案是阻止我对视图结构应用这样的条件修饰符和扩展,因为如果可能的话,我肯定更喜欢更自然和更好的方法。
我是 Swift 的新手,所以也许有更简单或更好的解决方案,我愿意遵循最佳实践。
在这种情况下,我怎么能设法完成我想要的?
谢谢您的帮助。
附加信息
我目前正在使用 List 上的.onDelete和.onMove实现以及 SwiftUI 版本模式,我想继续使用它们。
我正在使用 Swift 语言和 SwiftUI 框架为最低iOS 14开发应用程序。
如果您需要更多代码示例或更好的解释,请随时提出,我将编辑我的问题。
最小的工作示例
Yrb 问道。
import SwiftUI
import PlaygroundSupport
extension View {
@ViewBuilder func `if`<Content: View>(_ condition: Bool, transform: (Self) -> Content) -> some View {
if condition {
transform(self)
} else {
self
}
}
}
struct SomeList: Identifiable {
let id: UUID = UUID()
var name: String
}
let someList: [SomeList] = [
SomeList(name: "row1"),
SomeList(name: "row2"),
SomeList(name: "row3")
]
struct ContentView: View {
@State private var editMode = EditMode.inactive
@State private var isShowingEditionSheet: Bool = false
var body: some View {
NavigationView {
List {
ForEach(someList) { someElement in
NavigationLink(destination: EmptyView()) {
Text(someElement.name)
} //: NavigationLink
.if(editMode == .active) { view in
view.onTapGesture {
isShowingEditionSheet = true
}
}
} //: ForEach
.onDelete { (indexSet) in }
.onMove { (source, destination) in }
} //: List
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
EditButton()
}
}
.environment(\.editMode, $editMode)
.sheet(isPresented: $isShowingEditionSheet) {
Text("Edition sheet")
}
} //: NavigationView
}
}
PlaygroundPage.current.setLiveView(ContentView())