はじめに
iOS Advent Calendar 2020 の18日目になります!
今回は、WWDC20にて紹介されていましたUIMenuの新機能
について焦点を当ててみようと思います。
UIMenu
自体はiOS13から使用できるのですが、
iOS14からはUIButton
やUIBarButtonItem
にも使用可能となった内容がありましたので、おさらい的な紹介ができればと思います🙇♂️
とはいっても、すでにとても分かりやすくまとめられている記事がありましたので、細かい説明は割愛させていただきます。
本記事では、実際にコードで書いてみて使い心地を感じられればと思います。
UIMenu:
https://developer.apple.com/documentation/uikit/uimenu
WWDC20:
https://developer.apple.com/videos/play/wwdc2020/10205
1. 新機能について
すでに分かりやすくまとめられている記事が多々ありますのでリンクを貼らせていただきました🙇♂️
(これ以上上手にまとめられる気がしませんでした。。)
https://medium.com/better-programming/whats-new-in-ios-14s-uimenu-and-contextmenu-433cd2037c37
2. UIMenuのメリット
UIMenu
と比較する機能としてUIAlertController(ActionSheet)
が挙げられると思います。
WWDC20でもこちらを比較して紹介されていましたので以下にまとめてみました。
UIMenu
のメリットとして以下が挙げられていました。
- iPad表示するためにポップオーバー表示するための実装をする必要がない
- 表示の際に背景を暗くする処理がなくなったため、その分軽量感のある遷移になる
- 閉じるための「キャンセル」ボタンが不要(Menu外をタップすると閉じる)
- タップした箇所からMenuが表示されるので操作しやすい(操作性) etc...
3. 実際に書いてみました
「百聞は一見にしかず」と言うことで、実際にコードを書いてみました。
- [3-1]
HIGH, MID, LOW
を切り替える - [3-1]
UIMenu
を開いた際には、選択した項目にチェックマークがついている - [3-2]
UIMenu
を非同期で構築する場合
と言う内容を実装してみたいと思います。
3-1. 完成画面
3-1-1. 下準備
UIMenu
を設定する前に、設定するためのUIButton
などを下準備します。
class ViewController: UIViewController {
// メニュー表示項目
enum MenuType: String {
case high = "HIGH"
case mid = "MID"
case low = "LOW"
}
// メニュー選択ボタン
@IBOutlet weak var menuButton: UIButton!
// 選択されたMenuType
var selectedMenuType = MenuType.high
override func viewDidLoad() {
super.viewDidLoad()
}
}
3-1-2. UIButtonにUIMenuを設定
UIMenu
をUIButton
に設定するために、以下のメソッドを作成しました。
private func configureMenuButton() {
var actions = [UIMenuElement]()
// HIGH
actions.append(UIAction(title: MenuType.high.rawValue, image: nil, state: self.selectedMenuType == MenuType.high ? .on : .off,
handler: { (_) in
self.selectedMenuType = .high
// UIActionのstate(チェックマーク)を更新するためにUIMenuを再設定する
self.configureMenuButton()
}))
// MID
actions.append(UIAction(title: MenuType.mid.rawValue, image: nil, state: self.selectedMenuType == MenuType.mid ? .on : .off,
handler: { (_) in
self.selectedMenuType = .mid
// UIActionのstate(チェックマーク)を更新するためにUIMenuを再設定する
self.configureMenuButton()
}))
// LOW
actions.append(UIAction(title: MenuType.low.rawValue, image: nil, state: self.selectedMenuType == MenuType.low ? .on : .off,
handler: { (_) in
self.selectedMenuType = .low
// UIActionのstate(チェックマーク)を更新するためにUIMenuを再設定する
self.configureMenuButton()
}))
// UIButtonにUIMenuを設定
menuButton.menu = UIMenu(title: "", options: .displayInline, children: actions)
// こちらを書かないと表示できない場合があるので注意
menuButton.showsMenuAsPrimaryAction = true
// ボタンの表示を変更
menuButton.setTitle(self.selectedType.rawValue, for: .normal)
}
3-1-3. 完成
あとは、作成したメソッドをviewDidLoad
内で呼び出す様にします。
class ViewController: UIViewController {
// メニュー表示項目
enum MenuType: String {
case high = "HIGH"
case mid = "MID"
case low = "LOW"
}
@IBOutlet weak var selectButton: UIButton!
// 選択されたMenuType
var selectedType = MenuType.high
override func viewDidLoad() {
super.viewDidLoad()
// UIButtonにUIMenuを設定する
self.configureMenuButton()
}
}
3-2. 非同期でUIMenuを構築する場合
非同期でUIMenu
の構築が必要な場合は、UIDeferredMenuElement
を使用することで実装することができます。
var actions = [UIMenuElement]()
let deferredMenuElement = UIDeferredMenuElement({ completion in
// 時間がかかる処理
....
completion(actions)
})
menuButton.menu = UIMenu(title: "UIDeferredMenuElement", options: .displayInline, children: [deferredMenuElement])
menuButton.showsMenuAsPrimaryAction = true
UIDeferredMenuElement:
https://developer.apple.com/documentation/uikit/uideferredmenuelement
4. まとめ
これらの実装は、iOS14から対応なので実際にはiOS13以下の場合の分岐処理を書かなければならず、面倒ではあります。
ただ、操作感が良くや使い所が多岐に渡りそうな気がするのでとても魅力的な機能でした。
個人的にはUIAlertController(ActionSheet)
実装時にiPad用に処理を書き忘れてしまうと、クラッシュする原因にもなるのでこの辺り考えなくて良くなるのは嬉しい部分ではありました。
実装してみましたが、説明やコードで誤りなどございましたらご指摘いただけると幸いです。
ご協力のほどよろしくお願いいたします🙇♂️