iOSにNSPopupButtonが無いので…

UIStackView → UIButton とか、UITableView ベースのクラスとかでそれっぽいのを実装しようとしたけど、微妙に望むものができない。

が、いつまでもこだわっていると先に進めないので、ひとまず UIMenuController とやらを試すことにした・・・

試したが表示されない。

やりたいこととしては UIButton が押されたタイミングで UIMenuController を表示したいけど、表示されない。

SetTargetRect, setMenuVisible, canBecomeFirstResponder, becomeFirstResponder, canPerformAction とか色々書いたけど駄目。

二進も三進もいかないのでコツコツとデバッグログ入れてみると・・・

cut:
copy:
select:
selectAll:
paste:
delete:
_promptForReplace:
_transliterateChinese:
_showTextStyleOptions:
_lookup:
_define:
_addShortcut:
_accessibilitySpeak:
_accessibilitySpeakLanguageSelection:
_accessibilityPauseSpeaking:
_share:
makeTextWritingDirectionRightToLeft:
makeTextWritingDirectionLeftToRight:

canPerformAction で渡されてくる action が上記のもので、本来メニューで表示したかった #selector で指定したものが渡ってこない。

最初よく考えもせずに UIMenuItem の追加を以下で書いていた

let item1 = UIMenuItem(title: "Google", action: #selector(MenuButton.google))
let item2 = UIMenuItem(title: "Amazon", action: #selector(MenuButton.amazon))
menu.menuItems?.append(item1)
menu.menuItems?.append(item2)

何のことはなく、menuItems が nil なので nil に append しても nil な訳で・・・また基礎的なところでつまづいてしまった;

自動で補完される ? に早く気付けばよかった・・・

動くコードは以下。

class MenuButton: UIButton {
    override var canBecomeFirstResponder: Bool { return true }
    func showMenu(_ sender: Any?) {
        self.becomeFirstResponder()
        let menu = UIMenuController.shared
        if !menu.isMenuVisible {
            menu.arrowDirection = .down
            let items:Array<UIMenuItem> = [
                UIMenuItem(title: "Google", action: #selector(MenuButton.google)),
                UIMenuItem(title: "Amazon", action: #selector(MenuButton.amazon))
            ]
            menu.menuItems = items
            menu.setTargetRect(self.bounds, in: self)
            menu.setMenuVisible(true, animated: true)
        }
    }
    override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
        print("\(action)")
        if action == #selector(google) || action == #selector(amazon) {
            return true
        }
        return false
    }
    func google() {
        print("Google")
    }
    func amazon() {
        print("Amazon")
    }
}

呼び出す場合は

MenuButton.showMenu()

StoryBoard 経由ならクラス指定を MenuButton にして、

以下を追記してボタンと Touch Up Inside 紐づける。

@IBAction func buttonTouch(sender: Any) {
    let button = sender as! MenuButton
    button.showMenu(sender)
}

フォローする