我的应用程序中有一个菜单,当菜单打开时我会触发触觉反馈(来自
onTagGesture
操作)。
但是有时当我点击菜单打开触发器时,菜单实际上不会打开,但我仍然收到触觉反馈。我只在菜单实际打开时才需要触觉。
这是代码简化的代码块:
Menu {
Button("Menu button", action: someAction}}
} label: {
Text("Open menu") //in reality, this a more complicated view tapping on which should open the (context) menu
}
.onTagGesture {
let generator = UIImpactFeedbackGenerator(style: .rigid)
generator.impactOccurred()
}
所以这非常简单 - 点击
Open menu
触发器,点击被注册,触觉被回放,菜单打开。
但如前所述,无论出于何种原因,有时我按下
Open menu
元素,触觉会回放,但实际菜单不会打开。
无论出于什么原因,我想知道一旦菜单实际打开(或者更好的是,will实际打开),是否有任何方法可以执行操作(例如前面提到的触觉反馈)?我尝试尽一切可能寻找,却一无所获。
这也很重要因为菜单也需要长按才能打开,这是打开菜单的iOS标准操作。即使我可以为长按添加另一个单独的处理程序(为两种情况提供触觉),但这似乎根本不是一个正确的方法。
结合有时菜单无法打开的事实,我似乎确实需要其他解决方案。任何人都可以分享任何想法吗?我是否缺少某种 onXXXXX 处理程序,该处理程序会在菜单打开时触发?
谢谢!
PS:为了提供更多细节,我正在尝试对 Apple 开发文档中描述的菜单实现这种方法: https://developer.apple.com/documentation/swiftui/menu
作为该过程的一部分,我尝试将 onAppear 处理程序附加到整个菜单以及菜单内的单个元素。似乎都不起作用。
Menu {
Button("Open in Preview", action: openInPreview)
Button("Save as PDF", action: saveAsPDF)
.onAppear { doHaptic() } //only fires once, when menu opens, but not for subsequent appearances
} label: {
Label("PDF", systemImage: "doc.fill")
}
.onAppear { doHaptic() } //doesn't really as it fires when the menu itself appears on the screen as a child of a parent view.
以下变体似乎有效(使用 Xcode 13.3 / iOS 15.4 测试)
Menu {
Button("Open in Preview", action: {})
Button("Save as PDF", action: {})
} label: {
Label("PDF", systemImage: "doc.fill")
}
.contentShape(Rectangle())
.simultaneousGesture(TapGesture().onEnded {
print(">> tapped")
})
*但是,请注意,手势不仅在点击打开时得到解决,而且在点击关闭时也得到解决。
Asperi 的答案在 XCode 15.1 和 iOS 17 上对我不起作用。但是,
primaryAction
有效:https://developer.apple.com/documentation/swiftui/menu#Primary-action
注意:我的用例位于
.toolbar { ... }
内
Menu {
Button("Open in Preview", action: {})
Button("Save as PDF", action: {})
} label: {
Label("PDF", systemImage: "doc.fill")
} primaryAction: {
print("primary action")
}
您可以使用
onAppear
来实现这一点。在菜单上使用它,只有当菜单出现时才会调用它。例如。如下:
struct ContentView: View {
@State var menuOpen: Bool = false
// Just your button that triggers the menu
var body: some View {
Button(action: {
self.menuOpen.toggle()
}) {
if menuOpen {
MenuView(menuOpen: $menuOpen)
} else {
Image(systemName: "folder")
}
}
}
}
struct MenuView: View {
@Binding var menuOpen: Bool
// the menu view
var body: some View {
Rectangle()
.frame(width: 200, height: 200)
.foregroundColor(Color.red)
.overlay(Text("Menu Open").foregroundColor(Color.white))
.onAppear(perform: self.impactFeedback) // <- Use onAppear on the menu view to trigger it
}
// if function called it triggers the impact
private func impactFeedback() {
let generator = UIImpactFeedbackGenerator(style: .rigid)
generator.impactOccurred()
print("triggered impact")
}
}
已在 Xcode 12.4 上测试并运行
我发现将菜单项包装在 VStack 中并向其添加
onAppear
/ onDisappear
将使您能够跟踪菜单状态。
Menu {
VStack {
ForEach(channels, id: \.id) { c in
Button {
self.channel = c
} label: {
Text(c.name)
}
}
}
.onAppear {
debugPrint("Shown")
}
.onDisappear {
debugPrint("Hidden")
}
} label: {
Image(systemName: "list.bullet")
}