Tutorial: Apps mit UIContextMenuInteraction um Kontextmenüs erweitern
In iOS 13 gibt es ein neues Standard-UI für Kontextmenüs, welches das alte Peek and Pop-Feature ersetzt. Diese werden auf 3D-Touch-fähigen Geräten durch einen kräftigen Force-Touch ausgelöst, auf allen anderen Geräten durch einen etwas länger gehaltenen Long Press:
Wird eine App mit Catalyst für macOS kompiliert, wird das Kontextmenü entsprechend ähnlich zu einer AppKit-Anwendung dargestellt:
Das Tutorial zeigt, wie ein UI mit einer UIContextMenuInteraction um ein solches Kontextmenü erweitert wird.
-
Verwende die aktuellste Xcode 11-Version (dieses Tutorial wurde zuletzt getestet am 27. Oktober 2019 mit Xcode 11.1).
-
Lade das Start-Beispielprojekt Countries.
Dieses Projekt basiert auf dem UICollectionView-Tutorial und enthält ein UI, welches im Folgenden um Kontextmenüs erweitert wird.
Mache Dich mit dem Beispielprojekt vertraut: GalleryCollectionViewController ist der Collection-View-Controller den die App initial anzeigt, CountryViewController ist die Folge-Sicht wenn eine Fotokachel getappt wird.
-
Erstelle für die Implementierung des Kontextmenüs eine neue Swift-Datei CountryContextMenus.swift.
-
Implementiere hier eine Funktion createContextMenu, die eine UIContextMenuConfiguration erstellt. Erzeuge ein UIMenu mit einer UIAction, die die Sehenswürdigkeit zu dem Land (repräsentiert durch ein Country) in der Apple Maps-App öffnet (dazu enthält das Projekt bereits eine Extension Country+Maps.swift, die das Öffnen in Maps erledigt):
private func contextMenu(for country: Country) -> UIContextMenuConfiguration { let actionProvider: UIContextMenuActionProvider = { (suggestedActions) in let openInMaps = UIAction(title: "Auf Karte zeigen", image: UIImage(systemName: "mappin.and.ellipse")) { (action) in country.openInMaps() } return UIMenu(title: country.name, children: [openInMaps]) } return UIContextMenuConfiguration(identifier: nil, previewProvider: nil, actionProvider: actionProvider) }
Tipp: Die Symbole stammen aus der neuen SF Symbols Schrift, die mit der SF Symbols-App nachgeschlagen werden können:
-
Dies sollte zu einem Compilerfehler 'UIContextMenuConfiguration' is only available in iOS 13.0 or newer führen. Du kannst dieses Problem beheben, indem Du die Funktion nur für iOS 13 deklarierst:
@available(iOS 13.0, *) private func contextMenu(for country: Country) -> UIContextMenuConfiguration { // ... }
Alternativ kannst Du auch das Mindest-Deployment-Target der App auf iOS 13 setzen:
-
Erstelle eine Extension für den CountryViewController, die diesen konform zu dem Protokoll UIContextMenuInteractionDelegate macht und implementiere die Methode contextMenuInteraction(_:, configurationForMenuAtLocation:):
@available(iOS 13.0, *) extension CountryViewController: UIContextMenuInteractionDelegate { func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? { guard let country = country else { return nil } return contextMenu(for: country) } }
-
Erstelle eine weitere Extension für den Controller und ergänze eine Methode enableContextMenu, die dem imageView des Controllers eine UIContextMenuInteraction setzt:
@available(iOS 13.0, *) extension CountryViewController { func enableContextMenu() { imageView.isUserInteractionEnabled = true imageView.addInteraction(UIContextMenuInteraction(delegate: self)) } }
-
So ist die Logik für das Kontext-Menü separat abgelegt, lediglich der Aufruf von enableContextMenu muss in der CountryViewController-Klasse selbst erfolgen:
class CountryViewController: UIViewController { // ... override func viewDidLoad() { super.viewDidLoad() if #available(iOS 13.0, *) { self.enableContextMenu() } } // ... }
-
Erstelle eine weitere Extension, um die Klasse GalleryCollectionViewController um die collectionView(_:, contextMenuConfigurationForItemAt:)-Methode zu erweitern (UITableViewController und UICollectionViewController unterstützen das Kontextmenü über eine solche Delegate-Methode):
@available(iOS 13.0, *) extension GalleryCollectionViewController { override func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? { let country = self.countries[indexPath.row] return contextMenu(for: country) } }
-
Starte die App und teste, dass das Kontextmenü in beiden View-Controllern angezeigt wird (Long Press auf eines der Fotos):
-
Sofern Du macOS Catalina verwendest: Konfiguriere die App im Target kompatibel zu Mac:
Wähle als Schema My Mac aus und starte die App. Hier wird das Kontextmenü als natives macOS-Kontextmenü angezeigt:
-
Aufgabe: Ergänze das Kontext-Menü um eine weitere UIAction, um das Foto zu teilen. Das Share-Sheet kann mit dem UIActivityViewController angezeigt werden:
let shareSheet = UIActivityViewController(activityItems: [country.image], applicationActivities: nil) self.present(shareSheet, animated: true, completion: nil)
Weitere Informationen
-
UIContextMenuInteractionDokumentation zur UIContextMenuInteraction-Klasse.
-
WWDC 2019: Modernizing Your UI for iOS 13 - UIContextMenuInteractionDer WWDC-Vortrag Modernizing Your UI for iOS 13 behandelt auch das Thema Kontext-Menüs.
-
The Comprehensive Guide to iOS Context MenusUmfangreiche Beispiele zu Kontextmenüs - insb. die Möglichkeiten, die Vorschau anzupassen.