iOS 14 来自 UIView 的上下文菜单(不是来自 UIButton 或 UIBarButtonItem)
iOS 14 Context Menu from UIView (Not from UIButton or UIBarButtonItem)
iOS 13/14 通过 UIContextMenuInteraction
:
显示上下文菜单的简单方法
anyUIView.addInteraction(UIContextMenuInteraction(delegate: self))
我的问题是它模糊了整个用户界面。此外,这只能通过 long-press/Haptic Touch 调用。
如果我不想模糊,有操作菜单。如图所示
https://developer.apple.com/documentation/uikit/menus_and_shortcuts/adopting_menus_and_uiactions_in_your_user_interface
这似乎没有模糊,但它似乎只附加到 UIButton
或 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,该菜单在长按时调用并且不会出现模糊?
经过一些实验,我能够像这样去除暗淡的模糊。您将需要一个实用方法:
extension UIView {
func subviews<T:UIView>(ofType WhatType:T.Type,
recursing:Bool = true) -> [T] {
var result = self.subviews.compactMap {[=10=] 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)
结果是一个简单的点击召唤菜单,看起来像这样:
所以如果你能摆脱这种方法,我认为它会更好。
我只能跟进 Matt 的回答——使用 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)
iOS 13/14 通过 UIContextMenuInteraction
:
anyUIView.addInteraction(UIContextMenuInteraction(delegate: self))
我的问题是它模糊了整个用户界面。此外,这只能通过 long-press/Haptic Touch 调用。
如果我不想模糊,有操作菜单。如图所示 https://developer.apple.com/documentation/uikit/menus_and_shortcuts/adopting_menus_and_uiactions_in_your_user_interface
这似乎没有模糊,但它似乎只附加到 UIButton
或 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,该菜单在长按时调用并且不会出现模糊?
经过一些实验,我能够像这样去除暗淡的模糊。您将需要一个实用方法:
extension UIView {
func subviews<T:UIView>(ofType WhatType:T.Type,
recursing:Bool = true) -> [T] {
var result = self.subviews.compactMap {[=10=] 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)
结果是一个简单的点击召唤菜单,看起来像这样:
所以如果你能摆脱这种方法,我认为它会更好。
我只能跟进 Matt 的回答——使用 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)