6

有一种简单的方法可以通过以下方式在 iOS 13/14 中显示上下文菜单UIContextMenuInteraction

anyUIView.addInteraction(UIContextMenuInteraction(delegate: self))

对我来说,问题在于它模糊了整个用户界面。此外,这只能通过长按/触觉触摸来调用。

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

在此处输入图像描述

这似乎没有模糊,但它似乎只附加到 aUIButton或 a UIBarButtonItem

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?

4

2 回答 2

6

经过一些实验,我能够像这样消除变暗模糊。您将需要一个实用方法:

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)

结果是一个简单的点击召唤菜单,看起来像这样:

在此处输入图像描述

因此,如果您可以摆脱这种方法,我认为它会好得多。

于 2021-03-27T02:37:26.447 回答
1

我只能跟进马特的回答——使用 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)
于 2022-01-20T11:15:15.233 回答