我有一个列表项的视图,该视图显示有关嵌入在导航链接中的任务的一些基本信息。
我想使用 navigationLink 中的按钮来切换task.isComplete而不进行任何导航。
到目前为止,这是我的代码:
var body: some View {
NavigationLink(destination: TaskDetailView()) {
HStack {
RoundedRectangle(cornerRadius: 5)
.frame(width: 15)
.foregroundColor(getColor(task: task))
VStack {
Text(task.name!)
.font(.headline)
Spacer()
}
Spacer()
Button(action: {
self.task.isComplete.toggle()
}) {
if task.isComplete == true {
Image(systemName: "checkmark.circle.fill")
} else {
Image(systemName: "circle")
}
}
.foregroundColor(getColor(task: task))
.font(.system(size: 22))
}
}
}
Run Code Online (Sandbox Code Playgroud)
当前,不会执行按钮操作,因为只要按下按钮,导航链接就会将您带到目标视图。我尝试将按钮放在 navigationLink 之外 - 这允许操作发生,但导航仍然发生。
有没有办法使这成为可能?
谢谢。
要达到您想要的结果,您必须在 .onTapGesture 处理程序中使用其他内容,而不是 NavigationLink 上的按钮。下面的例子对我有用。
尝试更换:
Button(action: {
self.task.isComplete.toggle()
}) {
if task.isComplete == true {
Image(systemName: "checkmark.circle.fill")
} else {
Image(systemName: "circle")
}
}
Run Code Online (Sandbox Code Playgroud)
和:
Image(systemName: task.isComplete ? "checkmark.circle.fill" : "circle")
.onTapGesture { self.task.isComplete.toggle() }
Run Code Online (Sandbox Code Playgroud)
我遇到了同样的问题,这个问题基本上是我搜索解决方案时的第一个问题。因此,找到了一个相当优雅的解决方案,这似乎是一个离开它的好地方。
问题似乎NavigationLink是安装了's 手势,以便忽略其子视图手势:它有效地.gesture(_:including:)与 a GestureMaskof一起使用.gesture,这意味着它将优先于任何具有类似优先级的内部手势。
最终你需要的是一个.highPriorityGesture()安装了一个按钮来触发它的动作,同时保持按钮的常用 API:传递一个单一的动作方法来在它被触发时运行,而不是定义一个普通的 Image 并摆弄一个手势识别器来改变状态,等等。要使用标准Button行为和 API,您需要更深入一些,并直接定义一些按钮的行为。令人高兴的是,SwiftUI 为此提供了合适的钩子。
有两种方法可以自定义按钮的外观:您可以实现 aButtonStyle或 a PrimitiveButtonStyle,然后将其传递给.buttonStyle()修饰符。这两种协议类型都允许您定义将样式应用于按钮Label视图的方法,无论定义为什么。在常规中ButtonStyle,您会收到标签视图和一个isPressed布尔值,允许您根据按钮的触摸状态更改外观。APrimitiveButtonStyle提供了更多控制,但必须自己跟踪触摸状态:您收到标签视图和一个trigger()回调,您可以调用它来触发按钮的操作。
后者是我们在这里想要的:我们将拥有按钮的手势并能够跟踪触摸状态并确定何时触发它。然而,对于我们的用例来说,重要的是,我们负责将该手势附加到视图上——这意味着我们可以使用.highPriorityGesture()修饰符来做到这一点,并赋予按钮比链接本身更高的优先级。
对于一个简单的按钮,这实际上很容易实现。这是一个简单的按钮样式,它使用内部视图来管理触摸/按下状态,使按钮在触摸时半透明,并使用高优先级手势来实现按下/触发状态更改:
struct HighPriorityButtonStyle: PrimitiveButtonStyle {
func makeBody(configuration: PrimitiveButtonStyle.Configuration) -> some View {
MyButton(configuration: configuration)
}
private struct MyButton: View {
@State var pressed = false
let configuration: PrimitiveButtonStyle.Configuration
var body: some View {
let gesture = DragGesture(minimumDistance: 0)
.onChanged { _ in self.pressed = true }
.onEnded { value in
self.pressed = false
if value.translation.width < 10 && value.translation.height < 10 {
self.configuration.trigger()
}
}
return configuration.label
.opacity(self.pressed ? 0.5 : 1.0)
.highPriorityGesture(gesture)
}
}
}
Run Code Online (Sandbox Code Playgroud)
工作发生在内部MyButton视图类型中。它维护一个pressed状态,定义一个拖动手势(用于跟踪向下/向上/结束事件),trigger()该手势切换该状态并在手势结束时从样式配置调用该方法(假设它仍然看起来像“点击”)。然后它返回按钮提供的标签(即label: { }原始参数的内容Button),并附加两个新的修饰符: an .opacity()of1或0.5取决于按下状态,以及定义的手势作为 a .highPriorityGesture()。
如果您不想为触地状态提供不同的外观,这可以更简单一些。如果没有的需求@State属性来保存,你可以做的一切内部makeBody()执行:
struct StaticHighPriorityButtonStyle: PrimitiveButtonStyle {
func makeBody(configuration: PrimitiveButtonStyle.Configuration) -> some View {
let gesture = TapGesture()
.onEnded { _ in configuration.trigger() }
return configuration.label
.opacity(pressed ? 0.5 : 1.0)
.highPriorityGesture(gesture)
}
}
Run Code Online (Sandbox Code Playgroud)
最后,这是我用来测试上面第一个实现的预览。在实时预览中运行,您会在触地时看到按钮文本变暗:
struct HighPriorityButtonStyle_Previews: PreviewProvider {
static var previews: some View {
NavigationView {
List {
NavigationLink(destination: Text("Hello")) {
Button(action: { print("hello") }) {
Text("Button!")
.foregroundColor(.accentColor)
}
.buttonStyle(HighPriorityButtonStyle())
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,如果您想在用户开始拖动等时取消手势,则需要做更多的工作。这是另一回事。但是,pressed根据手势值的translation属性更改状态相对简单,并且仅在按下时结束时才触发。我将把它留给读者作为练习
添加.allowsHitTesting(false)到您的Button,当用户点击它时,NavigationLink 将被触发。
您拥有的任何按钮状态(例如活动样式)仍然有效。
NavigationLink(destination: TheBestViewEver(), label: {
Button("My Button", action: {
})
.allowsHitTesting(false)
})
Run Code Online (Sandbox Code Playgroud)
适用于 Xcode 14.1、Swift 5、iOS 15.0+。可能适用于旧版本。
| 归档时间: |
|
| 查看次数: |
3427 次 |
| 最近记录: |