iOS開發(fā) UIMenu:綜合使用指南

了解如何使用 UIMenu 構(gòu)建現(xiàn)代 UI。本教程展示了基本示例、如何添加分隔符、如何使用子菜單等等。
UIMenu 是超級多功能的組件,它看起來很現(xiàn)代,有很酷的動畫和很多自定義選項。

基本 UI 菜單

讓我們從基本菜單開始,然后我們可以在此基礎(chǔ)上進行構(gòu)建。因為UIMenu也在 Mac 上使用,所以有些事情我們會忽略。我會指出這些。

在創(chuàng)建菜單之前,我們需要在其中顯示一些項目。這些都是UIMenuElement類型。這是一種傘式類型,涵蓋了可以作為菜單元素的所有內(nèi)容。

在我們的例子中,這將是主要的UIAction,但UIMenu也會被考慮UIMenuElement。但我們不要操之過急。

另外要記住的關(guān)鍵一點是,我們沒有以與該方法UIMenu類似的方式進行顯示。相反,我們提前定義何時應(yīng)顯示菜單。我們稍后會討論這個。UIAlertControllerpresent

定義菜單的 UIAction

我們可以像這樣創(chuàng)建基本菜單項:

let refreshItem = UIAction(title: "Refresh", image: UIImage(systemName: "arrow.clockwise")) { (_) in
     // handle refresh
}

它init需要許多參數(shù),但只有titlehandler是必需的。SF 符號在這里效果很好,我的大多數(shù)菜單項都有 SF 符號集。第一個菜單項準(zhǔn)備好后,我們可以創(chuàng)建UIMenu

let menu = UIMenu(title: "Options", children: [refreshItem])

再次init需要許多參數(shù),但它們是可選的。Mac 上使用它identifier來指定菜單是否是系統(tǒng)標(biāo)準(zhǔn)菜單之一,如“文件”、“編輯”等。

奇怪的是我們還可以指定imageoptions是 的類型UIMenu.Options。在這種情況下他們不會做任何事情,我們稍后會介紹這些。

讓我們創(chuàng)建第二個操作并看看菜單是什么樣子的。

let deleteItem = UIAction(title: "Delete", image: UIImage(systemName: "trash"), attributes: .destructive) { (_) in
            // delete item
 }

這里我們也使用attributes參數(shù)來指示破壞性操作。這會使文本和圖標(biāo)變成紅色。您還可以用來.disabled指示某些選項當(dāng)前已禁用。

準(zhǔn)備好新操作后,我們可以更新菜單并查看它的外觀。

let menu = UIMenu(title: "Options", children: [refreshItem, deleteItem])

這是我們的實際菜單:


uimenu-basic-example.png

帶有分隔符和子菜單的 UIMenu

讓我們轉(zhuǎn)向更高級的東西。您實際上無法指定分隔符,但如果您創(chuàng)建子菜單,您會自動獲得這些分隔符。我們將使用現(xiàn)有的并對其進行修改。
我們可以準(zhǔn)備額外的物品

let favoriteAction = UIAction(title: "Favorite", image: UIImage(systemName: "star")) { (_) in
}     
let editAction = UIAction(title: "Edit", image: UIImage(systemName: "pencil")) { (_) in
}

然后是實際的菜單。

let submenu = UIMenu(title: "", options: .displayInline, children: [favoriteAction, editAction])

let menu = UIMenu(title: "Options", children: [deleteItem, refreshItem, submenu])

請注意,我們正在使用options參數(shù)來指定我們希望內(nèi)聯(lián)此菜單。這就是我們得到的:

uimenu-separator-example.png

如果我們不指定,.displayInline那么菜單將被嵌套,我們需要提供title和 ,理想情況image下,外觀與標(biāo)準(zhǔn)項目相同。

這是一個例子:

let submenu = UIMenu(title: "More", image: UIImage(systemName: "ellipsis"), children: [favoriteAction, editAction])

let menu = UIMenu(title: "Options", children: [submenu, deleteItem, refreshItem])

這是結(jié)果:


uimenu-submenu-example.gif

如果您想指示此菜單包含破壞性選項,可以使用.destructiveoptions參數(shù)。

UIAction 和狀態(tài)

創(chuàng)建的時候UIAction我們也可以指定state參數(shù)。您可以將其設(shè)置為.off、.on.mixed。

我發(fā)現(xiàn)只有.on表明選項處于活動狀態(tài)才有意義。文本前面會有復(fù)選標(biāo)記。在我的測試中也.mixed做了同樣的事情。這是我們的子菜單,其中最喜歡的操作狀態(tài)設(shè)置為.on

let favoriteAction = UIAction(title: "Favorite", image: UIImage(systemName: "star"), state: .on) { (_) in
}

結(jié)果:


uimenu-state-example.png

UIMenu 哪里有意義?

在我們開始向用戶顯示菜單之前,讓我們看看我們真正需要的地方。我認為導(dǎo)航欄是菜單最有意義的地方,因為空間有限。例如,如果用戶可以創(chuàng)建多個“事物”,您將有一個“+”按鈕,它將打開新菜單以供實際選擇。

例如,Apple Home 應(yīng)用程序就是這樣做的。


uimenu-uibarbutton-apple-home-example.png

或者,如果您有簡單的排序,您可以使用UIMenu讓用戶選擇應(yīng)按哪些項目進行排序。

請記住,當(dāng)用戶做出選擇時,菜單將始終消失。我認為它也是我們在 TableView 中使用的滑動操作的絕佳替代方案。TableView 和 CollectionView 本身都支持這些菜單。而且你還會得到一個很酷的動畫作為獎勵。

如何顯示 UIMenu

我們通過具有不同屬性、狀態(tài)等的菜單創(chuàng)建了各種類型。

讓我們看看如何實際向用戶顯示這些菜單。UIMenu從 iOS 13 開始,我們可以從 TableView 或 CollectionView 單元格中顯示,從 iOS 14 開始,我們還可以使用UIBarButtonItemplain UIButton。

顯示自UIBarButtonItem

我認為呈現(xiàn)菜單UIBarButtonItem將是相當(dāng)頻繁的用例,所以讓我們從它開始。在代碼中創(chuàng)建這些時,您可以使用新的初始值設(shè)定項,它將菜單作為參數(shù)之一。

navigationItem.rightBarButtonItem = UIBarButtonItem(title: nil, image: UIImage(systemName: "list.bullet"), primaryAction: nil, menu: menu)

主要操作的類型為UIAction。當(dāng)您傳遞 時nil,菜單將是該欄按鈕項目的主要操作。如果通過primaryAction,則只有長按按鈕后才會顯示菜單。

如果您正在創(chuàng)建更多標(biāo)準(zhǔn)UIBarButtonItem,那么可以使用init:

UIBarButtonItem(systemItem: .edit, primaryAction: nil, menu: menu)

這是結(jié)果:


uimenu-uibarbuttonitem-example.png

將 UIMenu 添加到 UICollectionViewCell

顯示菜單的另一個有用的地方是前面提到的集合視圖單元格。通過這種方式,您可以為用戶提供一種刪除項目、收藏項目等的方法,而無需進入詳細信息屏幕或單元格本身上有按鈕。

有兩個簡短的步驟可以實現(xiàn)此目的。首先您需要符合UICollectionViewDelegate.

然后實現(xiàn)這個方法:

func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {

}

根據(jù)提供的信息,indexPath您可以決定是否顯示菜單。如果您不想顯示菜單,只需返回即可nil。

如果你想顯示菜單,你可以返回如下內(nèi)容:

return UIContextMenuConfiguration(identifier: nil, previewProvider: nil, actionProvider: { _ in
   return UIMenu(title: "Options", children: [share, copy, delete])
})

然后,UIMenu當(dāng)長按集合視圖單元格時,您會得到這個很酷的動畫。這個例子來自我在GitHub上的開源項目。

uimenu-uicollectionviewcell-example.gif

將 UIMenu 添加到 UITableViewCell

TableView 的流程基本相同。您需要遵守UITableViewDelegate然后實現(xiàn)此方法:

func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {

} 

從 UIButton 顯示 UIMenu

演示的最后一個選項UIMenu來自UIButton。類似地,UIBarButtonItem您可以選擇長按后立即顯示菜單。

UIButton自 iOS 14 以來就有屬性menu,而且showsMenuAsPrimaryAction這是非常不言自明的。設(shè)置菜單,然后showsMenuAsPrimaryAction = true點擊按鈕后就會出現(xiàn)菜單。

showMenuButton.menu = menu
showMenuButton.showsMenuAsPrimaryAction = true
uimenu-uibutton-example.png

異步 UIMenu

我們要看的最后一件事是UIDeferredMenuElement。這適用于我們無法立即提供項目的情況UIMenuElement,但我們想讓用戶知道加載后會出現(xiàn)一些內(nèi)容。

雖然這個概念聽起來很復(fù)雜,但實際實施并不需要太多時間。當(dāng)您創(chuàng)建時,UIDeferredMenuElement有一個參數(shù)是一個閉包,它需要UIMenuElement.

這意味著我們需要加載項目、構(gòu)建菜單并將其交給閉包。iOS 將完成剩下的工作。這意味著它將顯示占位符加載指示器,然后自動顯示創(chuàng)建的菜單。

它還內(nèi)置了緩存機制,因此如果用戶多次打開菜單,項目將被緩存。

基本示例如下所示:

let asyncItem = UIDeferredMenuElement { (completion) in
      // load menu
}

當(dāng)然我們不必使用閉包,我們可以用prepare方法來代替:

func loadMenu(completion: @escaping (([UIMenuElement]) -> Void)) {
        // load menu
}

然后構(gòu)造代碼就更清晰了

let asyncItem = UIDeferredMenuElement(loadMenu(completion:))

出于演示目的,我們可以引入輕微的延遲,DispatchQueue然后查看操作中的菜單

func loadMenu(completion: @escaping (([UIMenuElement]) -> Void)) {
    DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
        let favoriteAction = UIAction(title: "Favorite", image: UIImage(systemName: "star"), state: .on) { (_) in
        }
        let editAction = UIAction(title: "Edit", image: UIImage(systemName: "pencil")) { (_) in
        }
        completion([favoriteAction, editAction])
    }
}

讓我們修改舊的菜單定義:

let asyncItem = UIDeferredMenuElement(loadMenu(completion:))
let menu = UIMenu(title: "Options", children: [asyncItem, refreshItem, deleteItem])

現(xiàn)在讓我們看看結(jié)果:


uimenu-asynchronous-deferred-menu-item-example.gif

結(jié)論

我認為這篇文章涵蓋了UIMenu. 這是一個很好的新組件,可以讓您構(gòu)建更現(xiàn)代的 UI。我們回顧了基礎(chǔ)知識、項目屬性、子菜單、“分隔符”、如何在屏幕上實際顯示這些菜單,我們還研究了延遲變體。

使用:Xcode 12Swift 5.3

翻譯自:https://nemecek.be/blog/88/uimenu-comprehensive-guide

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容