UIKit框架(三十二) —— 替換Peek and Pop交互的基于iOS13的Context Menus(一)

版本記錄

版本號(hào) 時(shí)間
V1.0 2019.12.28 星期六

前言

iOS中有關(guān)視圖控件用戶能看到的都在UIKit框架里面萧豆,用戶交互也是通過(guò)UIKit進(jìn)行的臼朗。感興趣的參考上面幾篇文章邻寿。
1. UIKit框架(一) —— UIKit動(dòng)力學(xué)和移動(dòng)效果(一)
2. UIKit框架(二) —— UIKit動(dòng)力學(xué)和移動(dòng)效果(二)
3. UIKit框架(三) —— UICollectionViewCell的擴(kuò)張效果的實(shí)現(xiàn)(一)
4. UIKit框架(四) —— UICollectionViewCell的擴(kuò)張效果的實(shí)現(xiàn)(二)
5. UIKit框架(五) —— 自定義控件:可重復(fù)使用的滑塊(一)
6. UIKit框架(六) —— 自定義控件:可重復(fù)使用的滑塊(二)
7. UIKit框架(七) —— 動(dòng)態(tài)尺寸UITableViewCell的實(shí)現(xiàn)(一)
8. UIKit框架(八) —— 動(dòng)態(tài)尺寸UITableViewCell的實(shí)現(xiàn)(二)
9. UIKit框架(九) —— UICollectionView的數(shù)據(jù)異步預(yù)加載(一)
10. UIKit框架(十) —— UICollectionView的數(shù)據(jù)異步預(yù)加載(二)
11. UIKit框架(十一) —— UICollectionView的重用蝎土、選擇和重排序(一)
12. UIKit框架(十二) —— UICollectionView的重用视哑、選擇和重排序(二)
13. UIKit框架(十三) —— 如何創(chuàng)建自己的側(cè)滑式面板導(dǎo)航(一)
14. UIKit框架(十四) —— 如何創(chuàng)建自己的側(cè)滑式面板導(dǎo)航(二)
15. UIKit框架(十五) —— 基于自定義UICollectionViewLayout布局的簡(jiǎn)單示例(一)
16. UIKit框架(十六) —— 基于自定義UICollectionViewLayout布局的簡(jiǎn)單示例(二)
17. UIKit框架(十七) —— 基于自定義UICollectionViewLayout布局的簡(jiǎn)單示例(三)
18. UIKit框架(十八) —— 基于CALayer屬性的一種3D邊欄動(dòng)畫(huà)的實(shí)現(xiàn)(一)
19. UIKit框架(十九) —— 基于CALayer屬性的一種3D邊欄動(dòng)畫(huà)的實(shí)現(xiàn)(二)
20. UIKit框架(二十) —— 基于UILabel跑馬燈類似效果的實(shí)現(xiàn)(一)
21. UIKit框架(二十一) —— UIStackView的使用(一)
22. UIKit框架(二十二) —— 基于UIPresentationController的自定義viewController的轉(zhuǎn)場(chǎng)和展示(一)
23. UIKit框架(二十三) —— 基于UIPresentationController的自定義viewController的轉(zhuǎn)場(chǎng)和展示(二)
24. UIKit框架(二十四) —— 基于UICollectionViews和Drag-Drop在兩個(gè)APP間的使用示例 (一)
25. UIKit框架(二十五) —— 基于UICollectionViews和Drag-Drop在兩個(gè)APP間的使用示例 (二)
26. UIKit框架(二十六) —— UICollectionView的自定義布局 (一)
27. UIKit框架(二十七) —— UICollectionView的自定義布局 (二)
28. UIKit框架(二十八) —— 一個(gè)UISplitViewController的簡(jiǎn)單實(shí)用示例 (一)
29. UIKit框架(二十九) —— 一個(gè)UISplitViewController的簡(jiǎn)單實(shí)用示例 (二)
30. UIKit框架(三十) —— 基于UICollectionViewCompositionalLayout API的UICollectionViews布局的簡(jiǎn)單示例(一)
31. UIKit框架(三十一) —— 基于UICollectionViewCompositionalLayout API的UICollectionViews布局的簡(jiǎn)單示例(二)

開(kāi)始

首先看下主要內(nèi)容

通過(guò)上下文菜單(context menus)學(xué)習(xí)增強(qiáng)您的應(yīng)用程序,包括配置操作誊涯,添加圖像挡毅,嵌套子菜單,添加自定義預(yù)覽等暴构。

下面看下寫(xiě)作環(huán)境

Swift 5, iOS 13, Xcode 11

接著就是正文了跪呈。

隨著iOS 13的正式發(fā)布段磨,我們獲得了一個(gè)新的,簡(jiǎn)單耗绿,功能強(qiáng)大且簡(jiǎn)潔的用戶界面范例-上下文菜單(context menus)苹支。 上下文菜單取代了iOS 12之前使用的標(biāo)準(zhǔn)Peek and Pop交互,并將其進(jìn)一步發(fā)展误阻。 當(dāng)您點(diǎn)擊并按住受支持的視圖時(shí)债蜜,上下文菜單會(huì)提供一些內(nèi)容的預(yù)覽以及操作列表。 它們?cè)谡麄€(gè)iOS中得到了廣泛使用究反,例如在Photos應(yīng)用中寻定。 點(diǎn)按并按住照片會(huì)顯示一個(gè)上下文菜單,如下所示:

動(dòng)作列表和預(yù)覽都是可自定義的精耐。 點(diǎn)擊預(yù)覽將打開(kāi)照片狼速。 您可以自定義在上下文菜單中點(diǎn)擊預(yù)覽時(shí)發(fā)生的情況。

在本教程中卦停,您將構(gòu)建上下文菜單向胡,并通過(guò)以下方法將其推至極限:

  • 配置動(dòng)作。
  • 使用新的SF Symbols集合為操作設(shè)置圖像沫浆。
  • 帶有嵌套和嵌入式子菜單的簡(jiǎn)化菜單捷枯。
  • 使用相關(guān)信息構(gòu)建更好的自定義預(yù)覽。
  • 將上下文菜單添加到表視圖中的每個(gè)項(xiàng)目专执。

Exploring Vacation Spots

上下文菜單旨在使現(xiàn)有內(nèi)容引人注目且易于訪問(wèn)淮捆。 您將菜單添加到現(xiàn)有應(yīng)用程序Vacation Spots中。

在Xcode中打開(kāi)準(zhǔn)備好的項(xiàng)目本股,運(yùn)行應(yīng)用程序并開(kāi)始計(jì)劃下一個(gè)假期攀痊。

打開(kāi)應(yīng)用程序時(shí),您會(huì)看到一個(gè)表格視圖拄显,其中包含不同的目的地苟径。

輕按一個(gè)度假勝地可顯示有關(guān)目的地的重要信息。 您還可以添加景點(diǎn)評(píng)級(jí)躬审,在地圖上查看或進(jìn)入其Wikipedia頁(yè)面棘街。


Your First Context Menu

查看度假勝地時(shí),點(diǎn)擊Submit Rating按鈕承边。

該應(yīng)用程序顯示代表1-5星的幾個(gè)不同按鈕遭殉。 點(diǎn)擊您的選擇,然后Submit Your Rating博助。 現(xiàn)在险污,Submit Rating按鈕以及您選擇的分?jǐn)?shù)將變?yōu)?code>Update Rating。 再次點(diǎn)擊它可以更改或查看您的評(píng)分。

使用我們的應(yīng)用程序蛔糯,對(duì)于某些急切的世界旅行者而言拯腮,這可能會(huì)成為一個(gè)繁瑣的過(guò)程。 這是您第一個(gè)上下文菜單的理想選擇蚁飒。

返回Xcode动壤,打開(kāi)SpotInfoViewController.swift,在其中添加上下文菜單淮逻。

在文件底部狼电,添加以下擴(kuò)展:

// MARK: - UIContextMenuInteractionDelegate
extension SpotInfoViewController: UIContextMenuInteractionDelegate {
  func contextMenuInteraction(
    _ interaction: UIContextMenuInteraction,
    configurationForMenuAtLocation location: CGPoint)
      -> UIContextMenuConfiguration? {
    return UIContextMenuConfiguration(
      identifier: nil,
      previewProvider: nil,
      actionProvider: { _ in
        let children: [UIMenuElement] = []
        return UIMenu(title: "", children: children)
    })
  }
}

UIContextMenuInteractionDelegate協(xié)議是構(gòu)建上下文菜單的關(guān)鍵。它帶有一個(gè)必需required的方法-contextMenuInteraction(_:configurationForMenuAtLocation :)弦蹂,您剛剛通過(guò)創(chuàng)建并返回一個(gè)新的UIContextMenuConfiguration對(duì)象來(lái)實(shí)現(xiàn)該方法肩碟。

有很多事情要做,但是一旦完成凸椿,您將了解iOS中上下文菜單的基礎(chǔ)削祈。 UIContextMenuConfiguration初始化程序采用三個(gè)參數(shù):

  • 1) identifier - 標(biāo)識(shí)符:使用標(biāo)識(shí)符來(lái)跟蹤多個(gè)上下文菜單。
  • 2) PreviewProvider:返回UIViewController的閉包脑漫。如果將其設(shè)置為nil髓抑,則菜單的默認(rèn)預(yù)覽將顯示,這只是您點(diǎn)擊的視圖优幸。稍后您將使用它來(lái)顯示更吸引人的預(yù)覽吨拍。
  • 3) actionProvider:上下文菜單中的每個(gè)項(xiàng)目都是一個(gè)動(dòng)作。此閉包實(shí)際上是您構(gòu)建菜單的地方网杆。您可以使用UIActions和嵌套的UIMenus構(gòu)建UIMenu羹饰。該閉包采用UIKit提供的建議操作數(shù)組作為參數(shù)。這次碳却,您將忽略它队秩,因?yàn)槟牟藛螌⒕哂心约旱淖远x項(xiàng)目。

注意:上下文菜單使用現(xiàn)代的Swift界面昼浦,其閉包比UIKit的閉包要多得多馍资。 您將在本教程中編寫(xiě)的大多數(shù)代碼都大量使用了閉包。 這也是一種正常的Swift風(fēng)格关噪,即使用尾隨閉包語(yǔ)法(trailing closure syntax)鸟蟹,而忽略了調(diào)用中的最終參數(shù)名稱。 在本教程中使兔,您將看到其余的調(diào)用建钥。

您可能已經(jīng)注意到,您從未直接創(chuàng)建上下文菜單火诸。 相反锦针,您將始終創(chuàng)建一個(gè)UIContextMenuConfiguration對(duì)象荠察,系統(tǒng)將使用該對(duì)象來(lái)配置菜單中的項(xiàng)目置蜀。

通常奈搜,用于創(chuàng)建上下文菜單的UIMenu不需要標(biāo)題,因此您可以為其提供空白字符串盯荤。 但是馋吗,到目前為止,這將創(chuàng)建一個(gè)空菜單秋秤。 如果菜單中包含一些操作宏粤,它將更加有用。

contextMenuInteraction(_:configurationForMenuAtLocation :)下添加此方法:

func makeRemoveRatingAction() -> UIAction {
  // 1
  var removeRatingAttributes = UIMenuElement.Attributes.destructive
  
  // 2
  if currentUserRating == 0 {
    removeRatingAttributes.insert(.disabled)
  }
  
  // 3
  let deleteImage = UIImage(systemName: "delete.left")
  
  // 4
  return UIAction(
    title: "Remove rating",
    image: deleteImage,
    identifier: nil,
    attributes: removeRatingAttributes) { _ in 
      self.currentUserRating = 0 
    }
}

makeRemoveRatingAction()創(chuàng)建一個(gè)UIAction來(lái)刪除用戶的評(píng)分灼卢。 稍后绍哎,您將其添加為上下文菜單中的第一項(xiàng)。 您的代碼將逐步執(zhí)行以下操作:

  • 1) 動(dòng)作action可以具有一組影響其外觀和行為的屬性鞋真。 因?yàn)檫@是一個(gè)刪除操作崇堰,所以可以使用destructive菜單元素屬性。
  • 2) 如果currentUserRating0涩咖,則表示用戶沒(méi)有評(píng)級(jí)海诲。 沒(méi)有要?jiǎng)h除的內(nèi)容,因此您添加了disable屬性以禁用菜單項(xiàng)檩互。
  • 3) UIAction可以具有圖像特幔,并且iOS 13SF符號(hào)看起來(lái)特別好,因此您可以將UIImage(systemName :)初始化程序與新 SF Symbols應(yīng)用程序中的符號(hào)名稱一起使用闸昨。
  • 4) 創(chuàng)建并返回一個(gè)UIAction蚯斯。 它不需要標(biāo)識(shí)符,因?yàn)橐院蟛恍枰盟?當(dāng)用戶點(diǎn)擊此菜單項(xiàng)時(shí)饵较,將觸發(fā)handler閉包溉跃。

返回contextMenuInteraction(_:configurationForMenuAtLocation :),將聲明children變量的行替換為:

let removeRating = self.makeRemoveRatingAction()
let children = [removeRating]

這將創(chuàng)建刪除評(píng)級(jí)操作并將其放置在children數(shù)組中告抄。

太好了撰茎,您的上下文菜單現(xiàn)在可以執(zhí)行有用的操作!

接下來(lái)打洼,在viewDidLoad()的末尾添加以下內(nèi)容:

let interaction = UIContextMenuInteraction(delegate: self)
submitRatingButton.addInteraction(interaction)

要在點(diǎn)擊并按住視圖時(shí)顯示上下文菜單龄糊,可以使用addInteraction方法向該視圖添加UIContextMenuInteraction。 這將創(chuàng)建一個(gè)交互募疮,并將其添加到submitRatingButton炫惩。

您終于可以看到運(yùn)行中的上下文菜單了!

生成并運(yùn)行該應(yīng)用程序阿浓。 點(diǎn)擊并按住Update Rating他嚷。 如果您已經(jīng)添加了評(píng)分,則可以將其刪除。 如果不是筋蓖,則禁用Remove rating菜單項(xiàng)卸耘。

這是一個(gè)開(kāi)始,但是這個(gè)不起眼的上下文菜單還有很長(zhǎng)的路要走粘咖。 您已經(jīng)了解了上下文菜單中最重要的概念:

  • UIContextMenuInteraction:將上下文菜單添加到視圖蚣抗。
  • UIContextMenuConfiguration:使用操作構(gòu)建UIMenu并配置其行為。
  • UIContextMenuInteractionDelegate:管理上下文菜單的生命周期瓮下,例如構(gòu)建UIContextMenuConfiguration翰铡。

但是,對(duì)于更多的美學(xué)問(wèn)題讽坏,例如自定義菜單的外觀呢锭魔?


Adding Submenus

子菜單是保持上下文菜單整潔有序的好方法。 使用它們對(duì)相關(guān)動(dòng)作進(jìn)行分組路呜。

將以下內(nèi)容添加到SpotInfoViewController.swift底部的UIContextMenuInteractionDelegate擴(kuò)展中:

func updateRating(from action: UIAction) {
  guard let number = Int(action.identifier.rawValue) else {
    return
  }
  currentUserRating = number
}

此方法使用UIAction的標(biāo)識(shí)符來(lái)更新用戶的評(píng)分赂毯。

UIContextMenuConfiguration一樣,UIAction可以具有標(biāo)識(shí)符拣宰。 updateRating(from :)嘗試將操作的標(biāo)識(shí)符轉(zhuǎn)換為Int并相應(yīng)地設(shè)置currentUserRating党涕。

updateRating(from :)下面添加以下方法:

func makeRateMenu() -> UIMenu {
  let ratingButtonTitles = ["Boring", "Meh", "It's OK", "Like It", "Fantastic!"]
  
  let rateActions = ratingButtonTitles
    .enumerated()
    .map { index, title in
      return UIAction(
        title: title,
        identifier: UIAction.Identifier("\(index + 1)"),
        handler: updateRating)
    }
  
  return UIMenu(
    title: "Rate...",
    image: UIImage(systemName: "star.circle"),
    children: rateActions)
}

此方法創(chuàng)建的UIAction具有與每個(gè)用戶等級(jí)匹配的標(biāo)識(shí)符:1到5。 請(qǐng)記住巡社,UIAction的處理程序是一個(gè)閉包項(xiàng)膛堤,在點(diǎn)擊該項(xiàng)目時(shí)會(huì)觸發(fā)它。 設(shè)置您之前寫(xiě)為每個(gè)操作處理程序的updateRating(from :)晌该。 然后肥荔,它返回一個(gè)帶有所有操作的UIMenu作為菜單的子級(jí)。

如果您查看UIActionUIMenu的聲明朝群,它們都是UIMenuElement的子類燕耿。 children參數(shù)的類型為[UIMenuElement]。 這意味著在配置上下文菜單時(shí)姜胖,可以同時(shí)添加操作或整個(gè)子菜單誉帅。

回到contextMenuInteraction(_:configurationForMenuAtLocation :),找到聲明子項(xiàng)的行右莱,并用以下內(nèi)容替換:

let rateMenu = self.makeRateMenu()
let children = [rateMenu, removeRating]

不是為每個(gè)可能的評(píng)分添加五個(gè)新項(xiàng)目蚜锨,而是將rateMenu添加為子菜單。

構(gòu)建并運(yùn)行該應(yīng)用程序慢蜓,并通過(guò)設(shè)置用戶等級(jí)來(lái)測(cè)試您的上下文菜單亚再。

1. Inline Menus

嵌套的子菜單可以清除內(nèi)容,但這意味著用戶需要額外點(diǎn)擊才能執(zhí)行操作晨抡。

為了使操作更簡(jiǎn)單氛悬,您可以內(nèi)聯(lián)顯示菜單则剃。 displayInline菜單選項(xiàng)將顯示根菜單中的所有項(xiàng)目,但用分隔線分隔如捅。

為此棍现,用以下命令替換創(chuàng)建并返回UIMenumakeRateMenu()的末尾:

return UIMenu(
  title: "Rate...",
  image: UIImage(systemName: "star.circle"),
  options: .displayInline,
  children: rateActions)

除了添加.displayInline菜單選項(xiàng)外,其他操作與以前相同伪朽。

構(gòu)建并運(yùn)行該應(yīng)用程序以查看結(jié)果:


Custom Previews

上下文菜單通常顯示內(nèi)容的預(yù)覽。 現(xiàn)在汛蝙,點(diǎn)擊并按住Submit Rating按鈕將顯示Submit Rating按鈕本身烈涮,或者其Update Rating更改自我,如上一個(gè)屏幕截圖所示窖剑。 這并不完全吸引人坚洽。

接下來(lái),您將設(shè)置自己的預(yù)覽西土。 在UIContextMenuInteractionDelegate擴(kuò)展的底部添加以下方法:

func makeRatePreview() -> UIViewController {
  let viewController = UIViewController()
  
  // 1
  let imageView = UIImageView(image: UIImage(named: "rating_star"))
  viewController.view = imageView
  
  // 2
  imageView.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
  imageView.translatesAutoresizingMaskIntoConstraints = false
  
  // 3
  viewController.preferredContentSize = imageView.frame.size
  
  return viewController
}

這使一個(gè)簡(jiǎn)單的UIViewController可以顯示一個(gè)星級(jí)讶舰。

這是逐步發(fā)生的事情:

  • 1) 該應(yīng)用程序已有一個(gè)rating_star圖像,用于該應(yīng)用程序中的星星需了。 使用該圖像創(chuàng)建一個(gè)UIImageView并將其設(shè)置為空白UIViewController的視圖跳昼。
  • 2) 設(shè)置圖像的frame以指定其尺寸。 設(shè)置frame就足夠了肋乍,您不需要為您設(shè)置任何“自動(dòng)布局”約束鹅颊。 將translatesAutoresizingMaskIntoConstraints設(shè)置為false
  • 3) 您需要指定preferredContentSize墓造,以將視圖控制器顯示為預(yù)覽堪伍。 如果您不這樣做,它將占用所有可用空間觅闽。

返回contextMenuInteraction(_:configurationForMenuAtLocation :)帝雇,找到UIContextMenuConfiguration初始化程序的PreviewProvider參數(shù)。 將以下行替換為將makeRatePreview作為預(yù)覽提供程序傳遞:

previewProvider: makeRatePreview) { _ in

構(gòu)建并運(yùn)行蛉拙。 點(diǎn)擊并按住Submit Rating按鈕后尸闸,您應(yīng)該會(huì)看到評(píng)分星標(biāo)的預(yù)覽:

做得好! 這樣就為該上下文菜單包裝了所有內(nèi)容孕锄。 現(xiàn)在室叉,您可以為完全不同的事情做準(zhǔn)備了。


Context Menus in Table Views

如果在主表視圖中點(diǎn)擊一個(gè)度假區(qū)會(huì)顯示一個(gè)常見(jiàn)操作列表硫惕,這是否有用茧痕? 查看度假勝地時(shí),View Map按鈕可打開(kāi)度假勝地所在位置的地圖視圖:

通過(guò)在度假勝地列表上的上下文菜單中添加View Map操作恼除,用戶可以在打開(kāi)景點(diǎn)信息視圖控制器之前先打開(kāi)地圖踪旷。 您還將添加一個(gè)操作曼氛,可輕松與朋友分享您最喜歡的度假勝地。 到目前為止令野,您已經(jīng)了解了向視圖添加上下文菜單的必要步驟:

  • 1) 將UIContextMenuInteraction添加到視圖舀患。
  • 2) 實(shí)現(xiàn)contextMenuInteraction(_:configurationForMenuAtLocation :),這是UIContextMenuInteractionDelegate的一種required方法气破。
  • 3) 使用所有菜單項(xiàng)構(gòu)建一個(gè)UIContextMenuConfiguration聊浅。

度假勝地列表以表格視圖的形式存在于SpotsViewController中。 表格視圖中的每一行都是視圖本身现使,或更具體地說(shuō)低匙,是UITableViewCell

要向每行添加上下文菜單碳锈,您可以像以前一樣進(jìn)行操作顽冶,但是有一種更簡(jiǎn)單的方法。

打開(kāi)SpotsViewController.swift并在類的底部添加以下代碼:

// MARK: - UITableViewDelegate

override func tableView(
  _ tableView: UITableView,
  contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint)
    -> UIContextMenuConfiguration? {
  // 1
  let index = indexPath.row
  let vacationSpot = vacationSpots[index]
  
  // 2
  let identifier = "\(index)" as NSString
  
  return UIContextMenuConfiguration(
    identifier: identifier, 
    previewProvider: nil) { _ in
      // 3
      let mapAction = UIAction(
        title: "View map",
        image: UIImage(systemName: "map")) { _ in
          self.showMap(vacationSpot: vacationSpot)
      }
      
      // 4
      let shareAction = UIAction(
        title: "Share",
        image: UIImage(systemName: "square.and.arrow.up")) { _ in
          VacationSharer.share(vacationSpot: vacationSpot, in: self)
      }
      
      // 5
      return UIMenu(title: "", image: nil, children: [mapAction, shareAction])
  }
}

使用表視圖售碳,將UIContextMenu添加到每一行就像在UITableViewDelegate上實(shí)現(xiàn)此方法一樣容易强重。

點(diǎn)擊并按住任意行將調(diào)用tableView(_:contextMenuConfigurationForRowAt:point :),從而允許它為特定行提供上下文菜單贸人。

您正在為表格視圖的每一行構(gòu)建一個(gè)有用的功能菜單间景,因此需要進(jìn)行很多工作。在上面的代碼中艺智,您:

  • 1) 獲取當(dāng)前行的休假地點(diǎn)拱燃。
  • 2) 在上下文菜單中添加一個(gè)標(biāo)識(shí)符,您將立即使用它力惯。您必須將其轉(zhuǎn)換為NSString碗誉,因?yàn)闃?biāo)識(shí)符需要符合NSCopying
  • 3) 菜單中的第一個(gè)動(dòng)作是針對(duì)地圖的父晶。輕觸此項(xiàng)目將調(diào)用showMap(vacationSpot :)哮缺,它將打開(kāi)該景點(diǎn)的地圖視圖。
  • 4) 添加其他動(dòng)作以共享該地點(diǎn)甲喝。 VacationSharer.share(vacationSpot:in :)是用于打開(kāi)共享表的幫助器方法尝苇。
  • 5) 最后,構(gòu)造并返回包含兩個(gè)項(xiàng)目的UIMenu埠胖。

就這樣糠溜。構(gòu)建并運(yùn)行該應(yīng)用程序,然后點(diǎn)擊并按住一個(gè)度假區(qū)以嘗試新的上下文菜單直撤。

1. Custom Previews in Table Views

再次非竿,默認(rèn)預(yù)覽留有一些改進(jìn)的余地。 上下文菜單僅將整個(gè)表格視圖單元格用作預(yù)覽谋竖。

在第一個(gè)上下文菜單中红柱,您使用了UIContextMenuConfigurationpreviewProvider參數(shù)來(lái)顯示自定義預(yù)覽承匣。 PreviewProvider使您可以創(chuàng)建一個(gè)全新的UIViewController作為預(yù)覽。 但是锤悄,還有另一種方式韧骗。

添加以下UITableViewDelegate方法:

override func tableView(_ tableView: UITableView,
  previewForHighlightingContextMenuWithConfiguration 
  configuration: UIContextMenuConfiguration)
    -> UITargetedPreview? {
  guard
    // 1
    let identifier = configuration.identifier as? String,
    let index = Int(identifier),
    // 2
    let cell = tableView.cellForRow(at: IndexPath(row: index, section: 0))
      as? VacationSpotCell
    else {
      return nil
  }
  
  // 3
  return UITargetedPreview(view: cell.thumbnailImageView)
}

這不是創(chuàng)建一個(gè)新的UIViewController,而是使用UITargetedPreview指定一個(gè)現(xiàn)有視圖零聚。 這是逐步發(fā)生的事情:

  • 1) 在前面的方法中袍暴,您為UIContextMenuConfiguration提供了一個(gè)標(biāo)識(shí)符。 現(xiàn)在隶症,您可以使用它來(lái)獲取分接索引政模。
  • 2) 獲取該索引處的單元格。
  • 3) 創(chuàng)建一個(gè)UITargetedPreview沿腰,并傳遞單元格的圖像視圖览徒。

這告訴上下文菜單將單元格的圖像視圖用作預(yù)覽狈定,而不是整個(gè)單元格颂龙。

構(gòu)建并運(yùn)行以查看新的預(yù)覽:

看起來(lái)好多了,不是嗎纽什? 現(xiàn)在措嵌,點(diǎn)擊預(yù)覽。

度假勝地列表再次在屏幕上顯示動(dòng)畫(huà)芦缰。 好吧企巢,預(yù)覽的目的是預(yù)覽某些內(nèi)容。 在這種情況下让蕾,預(yù)覽休假地點(diǎn)的地點(diǎn)信息視圖控制器將是有意義的浪规。 接下來(lái),您將解決這個(gè)問(wèn)題探孝。

2. Handling Preview Actions

SpotsViewController中添加此最終的UITableViewDelegate方法:

override func tableView(
  _ tableView: UITableView, willPerformPreviewActionForMenuWith
  configuration: UIContextMenuConfiguration,
  animator: UIContextMenuInteractionCommitAnimating) {
  // 1
  guard 
    let identifier = configuration.identifier as? String,
    let index = Int(identifier) 
    else {
      return
  }
  
  // 2
  let cell = tableView.cellForRow(at: IndexPath(row: index, section: 0))
  
  // 3
  animator.addCompletion {
    self.performSegue(
      withIdentifier: "showSpotInfoViewController",
      sender: cell)
  }
}

點(diǎn)擊上下文菜單的預(yù)覽時(shí)笋婿,將觸發(fā)此UITableViewDelegate方法。 輕按預(yù)覽可關(guān)閉上下文菜單顿颅,而tableView(_:willPerformPreviewActionForMenuWith:animator :)使您有機(jī)會(huì)在動(dòng)畫(huà)完成時(shí)運(yùn)行代碼缸濒。 下面就是要做的事情:

  • 1) 和以前一樣,使用標(biāo)識(shí)符查找上下文菜單所屬的行的索引粱腻。
  • 2) 獲取用戶點(diǎn)擊的單元格庇配。
  • 3) 動(dòng)畫(huà)(animator)對(duì)象處理釋放動(dòng)畫(huà)。 在這里绍些,您添加了一個(gè)完成處理程序捞慌,該處理程序通過(guò)segue顯示了現(xiàn)場(chǎng)信息視圖控制器。

構(gòu)建并運(yùn)行柬批,查看點(diǎn)擊預(yù)覽時(shí)會(huì)發(fā)生什么卿闹。 在學(xué)習(xí)過(guò)程中揭糕,使用新的上下文菜單會(huì)很有趣,因?yàn)槟呀?jīng)完成了本教程锻霎。 恭喜你著角!

上下文菜單還可以與drag and drop無(wú)縫地交互。 您可以通過(guò)觀看WWDC 2019的iOS 13的UI現(xiàn)代化視頻Modernizing Your UI for iOS 13了解更多信息旋恼。

后記

本篇主要講述了替換舊的Peek and Pop交互的基于iOS13的Context Menus吏口,感興趣的給個(gè)贊或者關(guān)注~~~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市冰更,隨后出現(xiàn)的幾起案子产徊,更是在濱河造成了極大的恐慌,老刑警劉巖蜀细,帶你破解...
    沈念sama閱讀 222,000評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件舟铜,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡奠衔,警方通過(guò)查閱死者的電腦和手機(jī)谆刨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)归斤,“玉大人痊夭,你說(shuō)我怎么就攤上這事≡嗬铮” “怎么了她我?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,561評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)迫横。 經(jīng)常有香客問(wèn)我番舆,道長(zhǎng),這世上最難降的妖魔是什么矾踱? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,782評(píng)論 1 298
  • 正文 為了忘掉前任恨狈,我火速辦了婚禮,結(jié)果婚禮上介返,老公的妹妹穿的比我還像新娘拴事。我一直安慰自己,他們只是感情好圣蝎,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布刃宵。 她就那樣靜靜地躺著,像睡著了一般徘公。 火紅的嫁衣襯著肌膚如雪牲证。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,394評(píng)論 1 310
  • 那天关面,我揣著相機(jī)與錄音坦袍,去河邊找鬼十厢。 笑死,一個(gè)胖子當(dāng)著我的面吹牛捂齐,可吹牛的內(nèi)容都是我干的蛮放。 我是一名探鬼主播,決...
    沈念sama閱讀 40,952評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼奠宜,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼包颁!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起压真,我...
    開(kāi)封第一講書(shū)人閱讀 39,852評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤娩嚼,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后滴肿,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體岳悟,經(jīng)...
    沈念sama閱讀 46,409評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評(píng)論 3 341
  • 正文 我和宋清朗相戀三年泼差,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了贵少。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片肿男。...
    茶點(diǎn)故事閱讀 40,615評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖抖部,靈堂內(nèi)的尸體忽然破棺而出龙宏,到底是詐尸還是另有隱情,我是刑警寧澤固蛾,帶...
    沈念sama閱讀 36,303評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響潜沦,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜绪氛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評(píng)論 3 334
  • 文/蒙蒙 一唆鸡、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧枣察,春花似錦争占、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,470評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至猿涨,卻和暖如春握童,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背叛赚。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,571評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工澡绩, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留稽揭,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,041評(píng)論 3 377
  • 正文 我出身青樓肥卡,卻偏偏與公主長(zhǎng)得像溪掀,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子步鉴,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容