SwiftUI菜单 - 当菜单实际打开时的操作

13

我有一个菜单在我的应用程序中,并且当菜单打开时(从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()
}

因此很简单-点击“打开菜单”触发器,点击被注册,触觉反馈播放,然后菜单就会打开。
但是正如所提到的,由于某些原因,有时我按下“打开菜单”元素,触觉反馈会播放,但实际菜单不会打开。
无论什么原因,我想知道是否有任何方法可以执行操作(例如上述的触觉反馈),一旦菜单实际上已经打开(或者更好的是,将要打开)?我尝试在所有可能的地方进行搜索,但毫无收获。
这也很重要,因为菜单还可以通过长按打开,这是iOS打开菜单的标准操作。尽管我可以添加另一个独立处理程序来提供两种情况的触觉反馈,但这似乎并不是正确的方法。
加上有时菜单不会打开的事实,我肯定需要其他解决方案。有人可以分享任何想法吗?我是否错过了某个当菜单即将打开时触发的onXXXXX处理程序?
谢谢!
附:为了提供更多细节,我正在尝试实现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.
3个回答

4
以下变量似乎可行(已在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")
    })

但是要注意的是,手势不仅在轻点打开时被解析,而且在轻点关闭时也会被解析。


3
不需要使用simultaneousGesture。只需一个简单的onTapGesture()即可完成工作。 - dknchris

0
你可以使用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 上测试并运行


4
谢谢,但那不是我想使用的结构。你提出了一个解决方案,适用于有一个按钮的情况,该按钮会显示或隐藏子视图。我认为这个解决方法可以正常工作,因为视图实际上是作为过程的一部分(并触发onAppear)而被构建和销毁的。我正在尝试为实际菜单结构获取触觉反馈,如此处所述: https://developer.apple.com/documentation/swiftui/menu如果我将onAppear处理程序放在Menu()结构本身上,它只会在菜单触发器作为父视图的一部分进入视图时触发一次。 - Mikhail Kornienko

0
我发现将菜单项包装在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")
}                                 

onDisappear在这里不起作用。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接