簡體   English   中英

SwiftUI:關閉 .contextMenu 時的通知 (iOS)

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM