底部菜单 点击突起_iOS开发之上下文交互菜单(UIContextMenuInteraction)
1. 概述
在iOS13及以后的版本,蘋果將用UIContextMenuInteraction取代上文中提到的Peek和Pop的功能,Peek和Pop的功能需要依賴硬件設備,UIContextMenuInteraction則擺脫了對硬件的依賴。
在iOS9以及iPhone6s及以上的設備上,蘋果推出了Peek和Pop功能,并在預覽時上滑提供可供操作的操作菜單。在iOS13及以后,蘋果禁用了UIViewControllerPreviewing協議相關方法,取而代之的則是UIContextMenuInteraction,如果項目最低運行版本是iOS13,且調用了UIViewControllerPreviewing協議相關方法,那么系統將會有黃色的警告,如果UIContextMenuInteraction和UIViewControllerPreviewing協議方法同時使用,系統只會采用UIContextMenuInteraction。
2. 上下文菜單(Context Menu)
在所有運行iOS13(及以上系統)的設備上,蘋果都為其提供了上下文菜單功能,用戶可以通過長按或者3D Touch(如果硬件支持)方式,彈出一個帶可操作菜單的預覽界面。
上下文菜單可以創建二級菜單,但是處于自動布局考慮,蘋果還是建議所有菜單盡量在同一級別。
下面看一下系統相冊列表長按圖片的效果
圖中,長按后彈出一個預覽視圖,底部帶有操作選項列表,周邊全部虛化,如果點擊預覽圖,則可進入全屏界面查看圖片。
2.1 一級菜單
要實現菜單的功能,則需要在控制器內實現UIContextMenuInteractionDelegate的協議方法:
func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration?該方法為協議中必須實現的一個方法,方法要求返回一個UIContextMenuConfiguration對象。這個對象配置了Context menu所需要的預覽圖以及操作事件等。具體看一下:
| var identifier: NSCopying | 初始化方法。 |
| typealias UIContextMenuContentPreviewProvider | 該block中返回預覽視圖控制器。 |
| typealias UIContextMenuActionProvider | 該block中返回可操作性的菜單。 |
舉個例子,在一個界面中,如下圖,按住button后彈出對圖片的操作選項。
在代理回調中創建一個UIContextMenuConfiguration對象,并添加對應的Action事件,代碼如下:
func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {return UIContextMenuConfiguration(identifier: nil, previewProvider: nil) { (elements) -> UIMenu? inlet favoriteAction = UIAction(title: "喜歡", image: UIImage(systemName: "heart.fill"), state: .off) { (action) in}let shareAction = UIAction(title: "分享", image: UIImage(systemName: "square.and.arrow.up.fill"), state: .off) { (action) in}let deleteAction = UIAction(title: "刪除", image: UIImage(systemName: "trash.fill"),attributes: [.destructive], state: .off) { (action) in}return UIMenu(title: "菜單", children: [favoriteAction, shareAction, deleteAction])} }然后還需要向button添加UIContextMenuInteraction對象,方可有長按住事件響應,代碼如下:
let interaction = UIContextMenuInteraction(delegate: self) button.addInteraction(interaction)當按住button后,效果如下:
以上則將Context Menu的一級菜單顯示出來了。
小結一下:
- UIContextMenuInteraction: 添加一個context menu到指定的view。
- UIContextMenuConfiguration: 配置一個帶有action列表的對象給context menu。
- UIContextMenuInteractionDelegate: 管理context menu的整個周期,彈出、展示、銷毀。
2.2 二級菜單
二級菜單能夠使Context Menu更加簡潔、清晰明了。
下面在上面一級菜單的基礎上增加一個打分功能二級菜單,代碼如下:
func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {return UIContextMenuConfiguration(identifier: nil, previewProvider: nil) { (elements) -> UIMenu? in// 二級action數組var ratingActions: Array<UIAction> = []// 遍歷增加5個actionfor i in 0..<5 {let action = UIAction(title: "(i+1) 分") { (action) in}ratingActions.append(action)}// 創建一個打分的菜單let ratingMenu = UIMenu(title: "打分", image: UIImage(systemName: "star.circle"), children: ratingActions)let favoriteAction = UIAction(title: "喜歡", image: UIImage(systemName: "heart.fill"), state: .off) { (action) in}let shareAction = UIAction(title: "分享", image: UIImage(systemName: "square.and.arrow.up.fill"), state: .off) { (action) in}let deleteAction = UIAction(title: "刪除", image: UIImage(systemName: "trash.fill"),attributes: [.destructive], state: .off) { (action) in}// 將打分的菜單放到一級菜單里面。return UIMenu(title: "菜單", children: [ratingMenu, favoriteAction, shareAction, deleteAction])} }運行效果圖如下,當點擊“打分”后,轉入二級菜單界面。
除了二級菜單,還可以有三級、四級等菜單,寫法就是層層嵌套,不過不建議搞這么多層的菜單,影響用戶體驗。
2.3 分組菜單
分組菜單可以將類似的功能歸到一組,比如上面將刪除單獨歸到一組,只需將刪除的action包裝到UIMenu中,并設置UIMenu的Options屬性為displayInline即可,代碼如下:
func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {return UIContextMenuConfiguration(identifier: nil, previewProvider: nil) { (elements) -> UIMenu? in// 二級action數組var ratingActions: Array<UIMenuElement> = []// 遍歷增加5個actionfor i in 0..<5 {let action = UIAction(title: "(i+1) 分") { (action) in}ratingActions.append(action)}// 創建一個打分的菜單let ratingMenu = UIMenu(title: "打分", image: UIImage(systemName: "star.circle"), children: ratingActions)let favoriteAction = UIAction(title: "喜歡", image: UIImage(systemName: "heart.fill"), state: .off) { (action) in}let shareAction = UIAction(title: "分享", image: UIImage(systemName: "square.and.arrow.up.fill"), state: .off) { (action) in}let deleteAction = UIAction(title: "刪除", image: UIImage(systemName: "trash.fill"),attributes: [.destructive], state: .off) { (action) in}// 創建一個delete menu,然后設置options屬性為displayInline,并將上面的deleteAction添加進來。let deleteMenu = UIMenu(title: "刪除菜單", options: .displayInline, children: [deleteAction])// 將打分的菜單放到一級菜單里面。return UIMenu(title: "菜單", children: [ratingMenu, favoriteAction, shareAction, deleteMenu])} }運行效果圖如下:
3. 預覽視圖及點擊處理
預覽視圖可以快速的給用戶提供一些預覽信息。
上面的操作菜單是通過UIContextMenuConfiguration對象UIContextMenuActionProvider提供呢,那么預覽視圖則是通過UIContextMenuConfiguration對象的[UIContextMenuContentPreviewProvider](https://developer.apple.com/documentation/uikit/uicontextmenucontentpreviewprovider)提供的,同樣是在創建UIContextMenuConfiguration對象的協議方法里面。
上面代碼中創建了一個DetailViewController對象,并給其傳遞數據,用于預覽內容,然后制定了預覽圖的大小。運行效果如下:
當點擊底部action列表項的時候,自有對應的action block處理點擊事件,那么如果點擊預覽圖,如何處理呢?下面看這個協議方法:
/*!* 當用戶點擊預覽圖的時候調用,相當于3DTouch里面的Pop功能。** @param interaction 當前的交互對象* @param configuration 當前的配置項* @param animator 跳轉動畫執行者,可以給它添加跳轉動畫。*/ func contextMenuInteraction(_ interaction: UIContextMenuInteraction, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating)下現在將這個協議方法具體實現以下:
func contextMenuInteraction(_ interaction: UIContextMenuInteraction, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) {animator.addCompletion { [weak self] inif let this = self {let detailVC = DetailViewController(nibName: "DetailViewController", bundle: nil)detailVC.image = this.button.imageView?.imagethis.show(detailVC, sender: nil)}} }添加了上面的方法后,在運行點擊預覽圖則會進入DetailViewController界面,全屏展示圖片。
4. TableView中的Context Menu
TableView中的Context Menu,系統已經進行了封裝,我們不需要在創建和添加UIContextMenuInteraction對象了,如果實現了協議方法,那么按住cell的時候就會響應協議方法。
相關協議方法如下:
Demo參考代碼如下:
override func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {let image = UIImage(named: imageArray[indexPath.row])return ImageContextMenuConfiguration.createInstance(indexPath.row, image)}override func tableView(_ tableView: UITableView, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) {if let index = Int(configuration.identifier as! String) {animator.addCompletion {[weak self]inlet detailVC = DetailViewController(nibName: "DetailViewController", bundle: nil)if let imageName = self?.imageArray[index] {detailVC.image = UIImage(named: imageName)}self?.show(detailVC, sender: nil)}} }5. CollectionView中的Context Menu
CollectionView中的Context Menu,系統已經進行了封裝,我們不需要在創建和添加UIContextMenuInteraction對象了,如果實現了協議方法,那么按住cell的時候就會響應協議方法。
相關協議方法如下:
func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? func collectionView(_ collectionView: UICollectionView, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating)Demo參考代碼如下:
override func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {let image = UIImage(named: imageArray[indexPath.row])return ImageContextMenuConfiguration.createInstance(indexPath.row, image)}override func collectionView(_ collectionView: UICollectionView, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) {if let index = Int(configuration.identifier as! String) {animator.addCompletion {[weak self]inlet detailVC = DetailViewController(nibName: "DetailViewController", bundle: nil)if let imageName = self?.imageArray[index] {detailVC.image = UIImage(named: imageName)}self?.show(detailVC, sender: nil)}} }6. 總結
干巴巴的講了一堆,如果不配上Demo,都有點說不過去,需要Demo的點擊這里。
Context Menu主要用于預覽和快速操作,功能和3D Touch差不多,但是有意取代3D Touch,以減少對硬件的依賴。
本文主要對該功能做了簡單的說明,至于如何將該功能用的更好,還待日后具體研究了,文中如果有不對的地方,還請路過的朋友指正。
更多可操作的協議方法,詳見**UIContextMenuInteractionDelegate。**
對了,蘋果建議Context Menu采用系統提供的圖片符號(SF Symbols),喜歡的小伙伴可以安裝看看,鏈接為:developer.apple.com/sf-symbols/,根據自己電腦系統,下載對應的SF Symbols。
本篇文章出自blog.csdn.net/guoyongming…的博客,如需轉載,請標明出處。
原文作者:Daniel_Coder
原文地址:https://blog.csdn.net/guoyongming925/article/details/110839969?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522160766080119721940249352%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=160766080119721940249352&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_v2~rank_v29-12-110839969.pc_search_result_cache&utm_term=iOS%E5%BC%80%E5%8F%91&spm=1018.2118.3001.4449
總結
以上是生活随笔為你收集整理的底部菜单 点击突起_iOS开发之上下文交互菜单(UIContextMenuInteraction)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 中one_十月中大型SUV销量排行:途昂
- 下一篇: spark sql 本地调试_干货 |