簡體   English   中英

iOS 14 來自 UIView 的上下文菜單(不是來自 UIButton 或 UIBarButtonItem)

[英]iOS 14 Context Menu from UIView (Not from UIButton or UIBarButtonItem)

有一種簡單的方法可以通過UIContextMenuInteraction在 iOS 13/14 中顯示上下文菜單:

anyUIView.addInteraction(UIContextMenuInteraction(delegate: self))

對我來說,問題在於它模糊了整個用戶界面。 此外,這只能通過長按/觸覺觸摸來調用。

如果我不想模糊,有操作菜單。 如此處所示https://developer.apple.com/documentation/uikit/menus_and_shortcuts/adopting_menus_and_uiactions_in_your_user_interface

在此處輸入圖像描述

這似乎沒有模糊,但它似乎只附加到UIButtonUIBarButtonItem

let infoButton = UIButton()
infoButton.showsMenuAsPrimaryAction = true
infoButton.menu = UIMenu(options: .displayInline, children: [])
infoButton.addAction(UIAction { [weak infoButton] (action) in
   infoButton?.menu = infoButton?.menu?.replacingChildren([new items go here...])
}, for: .menuActionTriggered)

有沒有辦法將上下文菜單附加到 UIView 長按時調用並且不會出現模糊?

經過一些實驗,我能夠像這樣消除變暗模糊。 您將需要一個實用方法:

extension UIView {
    func subviews<T:UIView>(ofType WhatType:T.Type,
        recursing:Bool = true) -> [T] {
            var result = self.subviews.compactMap {$0 as? T}
            guard recursing else { return result }
            for sub in self.subviews {
                result.append(contentsOf: sub.subviews(ofType:WhatType))
            }
            return result
    }
}

現在我們使用上下文菜單交互委托方法找到負責模糊的 UIVisualEffectView 並將其消除:

func contextMenuInteraction(_ interaction: UIContextMenuInteraction, willDisplayMenuFor configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionAnimating?) {
    DispatchQueue.main.async {
        let v = self.view.window!.subviews(ofType:UIVisualEffectView.self)
        if let v = v.first {
            v.alpha = 0
        }
    }
}

典型結果:

在此處輸入圖像描述

不幸的是,菜單后面現在根本沒有陰影,但它比大模糊要好。

當然,它仍然是一個長按手勢。 我懷疑對此無能為力! 如果這是一個普通的 UILongPressGestureRecognizer 你可能會找到它並縮短它的minimumPressDuration ,但它不是; 你必須讓自己遵守道路的 UIContextMenuInteraction 規則。


然而,說了這么多,如果可能的話,我可以想出一個更好的方法來做到這一點:讓這個 UIView成為UIControl。 現在它的行為就像一個 UIControl: 例如:

class MyControl : UIControl {
    override func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {
        let config = UIContextMenuConfiguration(identifier: nil, previewProvider: nil, actionProvider: { _ in
            let act = UIAction(title: "Red") { action in  }
            let act2 = UIAction(title: "Green") { action in  }
            let act3 = UIAction(title: "Blue") { action in  }
            let men = UIMenu(children: [act, act2, act3])
            return men
        })
        return config
    }
}

和:

let v = MyControl()
v.isContextMenuInteractionEnabled = true
v.showsMenuAsPrimaryAction = true
v.frame = CGRect(x: 100, y: 100, width: 200, height: 100)
v.backgroundColor = .red
self.view.addSubview(v)

結果是一個簡單的點擊召喚菜單,看起來像這樣:

在此處輸入圖像描述

因此,如果您可以擺脫這種方法,我認為它會好得多。

我只能跟進馬特的回答——使用 UIControl 更容易。 雖然沒有本機menu屬性,但有一種簡單的方法可以簡化contextMenuInteraction設置,只需創建 UIControl 的子類並將菜單傳遞到那里!

class MenuControl: UIControl {
    
    var customMenu: UIMenu
    
    // MARK: Initialization
    init(menu: UIMenu) {
        self.customMenu = menu
        super.init(frame: .zero)
        isContextMenuInteractionEnabled = true
        showsMenuAsPrimaryAction = true
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    // MARK: ContextMenu
    override func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {
        UIContextMenuConfiguration(identifier: nil, previewProvider: nil, actionProvider: { [weak self] _ in
            self?.customMenu
        })
    }
}

然后你只需要像這樣為 UIMenu 提供 UIActions:

let control = MenuControl(menu: customMenu)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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