Контекстное меню iOS 14 из UIView (не из UIButton или UIBarButtonItem)

Есть простой способ представить контекстное меню в iOS 13/14 через UIContextMenuInteraction:

anyUIView.addInteraction(UIContextMenuInteraction(delegate: self))

Проблема для меня в том, что он размывает весь пользовательский интерфейс. Кроме того, это вызывается только долгим нажатием / Haptic Touch.

Если я не хочу размытия, есть меню действий. Как показано здесь

введите здесь описание изображения

Это кажется без размытия, но кажется, что оно связано только с 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, которое вызывается при длительном нажатии и не отображается с размытием?


person Gizmodo    schedule 26.03.2021    source источник


Ответы (1)


После некоторых экспериментов мне удалось убрать размытие затемнения, вот так. Вам понадобится вспомогательный метод:

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 be 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)

И в результате простое нажатие вызывает меню, которое выглядит так:

введите здесь описание изображения

Так что, если вы можете избежать наказания за такой подход, я думаю, что это намного лучше.

person matt    schedule 27.03.2021
comment
Это очень информативный ответ! - person Gizmodo; 03.04.2021