[英]SwiftUI: Notification when .contextMenu is dismissed (iOS)
我在視圖.onDrag
和.contextMenu
一起使用,這似乎非常棘手:
通過將dragging
設置為 true,背景顏色變為灰色。 這是由.onDrag
觸發的,它在打開上下文菜單時已經發生(有點早但沒問題)。 當我使用按鈕關閉菜單時,我可以將dragging
設置為 false。 當我使用拖動時, dragging
state 在ItemProvider
被取消初始化時變回 false。 到目前為止,一切都很好。
問題
當我在上下文菜單外點擊以關閉它時,我似乎無法將dragging
state 設置回 false。 將.onDisappear
添加到菜單中的Button
不起作用。
我在這里做錯了什么? 有什么方法可以讓我在上下文菜單關閉時得到通知,或者在拖動實際開始時發生 state dragging
更改(以便在打開上下文菜單時背景不會立即變灰)?
視頻下方代碼。
struct ContentView: View {
@State var dragging = false
var body: some View {
ZStack {
Rectangle()
.foregroundColor(.blue)
.frame(width: 100, height: 100)
.onDrag {
dragging = true
let provider = ItemProvider(contentsOf: URL(string: "Test")!)!
provider.didEnd = {
DispatchQueue.main.async {
dragging = false
}
}
print("init ItemProvider")
return provider
}
.contextMenu {
Button("Close Menu") {
dragging = false
}
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(dragging ? Color.gray : Color.white)
}
}
class ItemProvider: NSItemProvider {
var didEnd: (() -> Void)?
deinit {
print("deinit ItemProvider")
didEnd?()
}
}
編輯(2022 年 12 月):代碼似乎適用於 iOS 16.2。 對於早期的 iOS 版本,我仍然沒有找到好的解決方案。
struct ContentView: View {
@State var dragging = false
var body: some View {
ZStack {
Rectangle()
.foregroundColor(.blue)
.frame(width: 100, height: 100)
.onDrag {
dragging = true
let provider = ItemProvider(contentsOf: URL(string: "Test")!)!
provider.didEnd = {
DispatchQueue.main.async {
dragging = false
}
}
print("init ItemProvider")
return provider
}
.contextMenu {
Button("Close Menu") {
dragging = false
}
}
.onTapGesture {
dragging = false
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(dragging ? Color.gray : Color.white)
}
}
class ItemProvider: NSItemProvider {
var didEnd: (() -> Void)?
deinit {
print("deinit ItemProvider")
didEnd?()
}
}
由於您提到 UIKit, .contextMenu
是一個UIContextMenuInteraction
您可以將UIContextMenuInteraction
添加到 SwiftUI View 並有權訪問UIContextMenuInteractionDelegate
以識別何時關閉菜單。
SwiftUI View > ViewModifier > UIViewRepresentable > Coordinator/UIContextMenuInteractionDelegate
struct CustomContextMenuView: View {
@State var dragging = false
var body: some View {
ZStack {
Rectangle()
.foregroundColor(.blue)
.frame(width: 100, height: 100)
.onDrag {
dragging = true
let provider = ItemProvider(contentsOf: URL(string: "Test")!)!
provider.didEnd = {
DispatchQueue.main.async {
dragging = false
}
}
print("init ItemProvider")
return provider
}
//Use custom context menu and add actions as [UIAction]
.contextMenu(actions: [
UIAction(title: "Close Menu", handler: { a in
print("Close Menu action")
dragging = false
})
], willEnd: {
//Called when the menu is dismissed
print("willEnd/onDismiss")
dragging = false
})
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(dragging ? Color.gray : Color.white)
}
}
可以通過將下面的代碼粘貼到.swift
文件中並像上面的示例一樣使用它來實現菜單。
extension View {
func contextMenu(actions: [UIAction], willEnd: (() -> Void)? = nil, willDisplay: (() -> Void)? = nil) -> some View {
modifier(ContextMenuViewModifier(actions: actions, willEnd: willEnd, willDisplay: willDisplay))
}
}
struct ContextMenuViewModifier: ViewModifier {
let actions: [UIAction]
let willEnd: (() -> Void)?
let willDisplay: (() -> Void)?
func body(content: Content) -> some View {
Interaction_UI(view: {content}, actions: actions, willEnd: willEnd, willDisplay: willDisplay)
.fixedSize()
}
}
struct Interaction_UI<Content2: View>: UIViewRepresentable{
typealias UIViewControllerType = UIView
@ViewBuilder var view: Content2
let actions: [UIAction]
let willEnd: (() -> Void)?
let willDisplay: (() -> Void)?
func makeCoordinator() -> Coordinator {
return Coordinator(parent: self)
}
func makeUIView(context: Context) -> some UIView {
let v = UIHostingController(rootView: view).view!
context.coordinator.contextMenu = UIContextMenuInteraction(delegate: context.coordinator)
v.addInteraction(context.coordinator.contextMenu!)
return v
}
func updateUIView(_ uiView: UIViewType, context: Context) {
}
class Coordinator: NSObject, UIContextMenuInteractionDelegate{
var contextMenu: UIContextMenuInteraction!
let parent: Interaction_UI
init(parent: Interaction_UI) {
self.parent = parent
}
func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {
UIContextMenuConfiguration(identifier: nil, previewProvider: nil, actionProvider: { [self]
suggestedActions in
return UIMenu(title: "", children: parent.actions)
})
}
func contextMenuInteraction(_ interaction: UIContextMenuInteraction, willDisplayMenuFor configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionAnimating?) {
print(#function)
parent.willDisplay?()
}
func contextMenuInteraction(_ interaction: UIContextMenuInteraction, willEndFor configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionAnimating?) {
print(#function)
parent.willEnd?()
}
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.