Replaces peek & pop with context menus on iOS 13+

This commit is contained in:
Riley Testut 2020-02-06 14:37:21 -08:00
parent 748f930186
commit cd7e9652ab
6 changed files with 289 additions and 59 deletions

View File

@ -129,6 +129,7 @@
BFDD04F11D5E2C27002D450E /* GameCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDD04F01D5E2C27002D450E /* GameCollectionViewController.swift */; };
BFE022A01F5B57FF0052D888 /* PopoverMenuButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFE0229F1F5B577D0052D888 /* PopoverMenuButton.swift */; };
BFE4269E1D9C68E600DC913F /* SaveStatesStoryboardSegue.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFE4269D1D9C68E600DC913F /* SaveStatesStoryboardSegue.swift */; };
BFE56E1923EB7BE00014FECD /* UIImage+SymbolFallback.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFE56E1823EB7BE00014FECD /* UIImage+SymbolFallback.swift */; };
BFE593CA21F3F8B7003412A6 /* GameSave.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFE593C921F3F8B7003412A6 /* GameSave.swift */; };
BFE593CC21F3F8C2003412A6 /* _GameSave.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFE593CB21F3F8C2003412A6 /* _GameSave.swift */; };
BFEE507D23E7612300416151 /* liblibDeSmuME.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BF76BDE823E649150073702C /* liblibDeSmuME.a */; };
@ -286,6 +287,7 @@
BFDF71DA22F94CDF0074D92E /* DSDeltaCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = DSDeltaCore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
BFE0229F1F5B577D0052D888 /* PopoverMenuButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopoverMenuButton.swift; sourceTree = "<group>"; };
BFE4269D1D9C68E600DC913F /* SaveStatesStoryboardSegue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SaveStatesStoryboardSegue.swift; sourceTree = "<group>"; };
BFE56E1823EB7BE00014FECD /* UIImage+SymbolFallback.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+SymbolFallback.swift"; sourceTree = "<group>"; };
BFE593C921F3F8B7003412A6 /* GameSave.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameSave.swift; sourceTree = "<group>"; };
BFE593CB21F3F8C2003412A6 /* _GameSave.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _GameSave.swift; sourceTree = "<group>"; };
BFEC732C1AAECC4A00650035 /* Roxas.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Roxas.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@ -351,6 +353,7 @@
BFFBD3D8224A0756002EFC79 /* URL+ExtendedAttributes.swift */,
BF647A6922FB8FCE0061D76D /* Bundle+SwizzleBundleID.swift */,
BFD1EF3F2336BD8800D197CF /* UIDevice+Processor.swift */,
BFE56E1823EB7BE00014FECD /* UIImage+SymbolFallback.swift */,
);
path = Extensions;
sourceTree = "<group>";
@ -1076,6 +1079,7 @@
BF797A2D1C2D339F00F1A000 /* UILabel+FontSize.swift in Sources */,
BF80E1D21F13117000847008 /* ControllerInputsViewController.swift in Sources */,
BF5942641E09BBB10051894B /* LoadControllerSkinImageOperation.swift in Sources */,
BFE56E1923EB7BE00014FECD /* UIImage+SymbolFallback.swift in Sources */,
BF6EE5E91F7C5F860051AD6C /* _GameControllerInputMapping.swift in Sources */,
BF5942871E09BC8B0051894B /* _ControllerSkin.swift in Sources */,
BF95E2791E4982A10030E7AD /* GamesDatabaseBrowserViewController.swift in Sources */,

View File

@ -17,8 +17,7 @@ extension Action
case destructive
case selected
var alertActionStyle: UIAlertAction.Style
{
var alertActionStyle: UIAlertAction.Style {
switch self
{
case .default, .selected: return .default
@ -27,8 +26,7 @@ extension Action
}
}
var previewActionStyle: UIPreviewAction.Style?
{
var previewActionStyle: UIPreviewAction.Style? {
switch self
{
case .default: return .default
@ -40,11 +38,40 @@ extension Action
}
}
@available(iOS 13, *)
extension Action.Style
{
var menuAttributes: UIMenuElement.Attributes {
switch self
{
case .default, .cancel, .selected: return []
case .destructive: return .destructive
}
}
var menuState: UIMenuElement.State {
switch self
{
case .default, .cancel, .destructive: return .off
case .selected: return .on
}
}
}
struct Action
{
let title: String
let style: Style
let action: ((Action) -> Void)?
var title: String
var style: Style
var image: UIImage? = nil
var action: ((Action) -> Void)?
init(title: String, style: Style = .default, image: UIImage? = nil, action: ((Action) -> Void)? = nil)
{
self.title = title
self.style = style
self.image = image
self.action = action
}
}
extension UIAlertAction
@ -82,6 +109,19 @@ extension UIAlertController
}
}
@available(iOS 13.0, *)
extension UIAction
{
convenience init?(_ action: Action)
{
guard action.style != .cancel else { return nil }
self.init(title: action.title, image: action.image, attributes: action.style.menuAttributes, state: action.style.menuState) { _ in
action.action?(action)
}
}
}
extension RangeReplaceableCollection where Iterator.Element == Action
{
var alertActions: [UIAlertAction] {
@ -93,4 +133,10 @@ extension RangeReplaceableCollection where Iterator.Element == Action
let actions = self.compactMap { UIPreviewAction($0) }
return actions
}
@available(iOS 13.0, *)
var menuActions: [UIAction] {
let actions = self.compactMap { UIAction($0) }
return actions
}
}

View File

@ -41,7 +41,8 @@ class PreviewGameViewController: DeltaCore.GameViewController
emulatorCore.addObserver(self, forKeyPath: #keyPath(EmulatorCore.state), options: [.old], context: &kvoContext)
self.preferredContentSize = emulatorCore.preferredRenderingSize
let size = CGSize(width: emulatorCore.preferredRenderingSize.width * 2.0, height: emulatorCore.preferredRenderingSize.height * 2.0)
self.preferredContentSize = size
}
}
@ -75,7 +76,7 @@ extension PreviewGameViewController
super.viewDidAppear(animated)
self.emulatorCoreQueue.async {
self.emulatorCore?.resume()
self.emulatorCore?.start()
}
}
@ -113,13 +114,9 @@ extension PreviewGameViewController
if previousState == .stopped, state == .running
{
self.emulatorCoreQueue.sync {
if self.isAppearing
{
// Pause to prevent it from starting before visible (in case user peeked slowly)
self.emulatorCore?.pause()
}
self.emulatorCoreQueue.async {
// Pause to prevent it from starting before visible (in case user peeked slowly)
self.emulatorCore?.pause()
self.preparePreview()
}
}
@ -172,5 +169,7 @@ private extension PreviewGameViewController
// Re-enable emulatorCore to update gameView again
self.emulatorCore?.add(self.gameView)
self.emulatorCore?.resume()
}
}

View File

@ -0,0 +1,24 @@
//
// UIImage+SymbolFallback.swift
// Delta
//
// Created by Riley Testut on 2/5/20.
// Copyright © 2020 Riley Testut. All rights reserved.
//
import UIKit
extension UIImage
{
convenience init?(symbolNameIfAvailable name: String)
{
if #available(iOS 13, *)
{
self.init(systemName: name)
}
else
{
return nil
}
}
}

View File

@ -8,6 +8,7 @@
import UIKit
import MobileCoreServices
import AVFoundation
import DeltaCore
@ -59,8 +60,9 @@ class GameCollectionViewController: UICollectionViewController
private let prototypeCell = GridCollectionViewCell()
private var _performing3DTouchTransition = false
private weak var _destination3DTouchTransitionViewController: UIViewController?
private var _performingPreviewTransition = false
private weak var _previewTransitionViewController: PreviewGameViewController?
private weak var _previewTransitionDestinationViewController: UIViewController?
private var _renameAction: UIAlertAction?
private var _changingArtworkGame: Game?
@ -92,27 +94,31 @@ extension GameCollectionViewController
layout.itemWidth = 90
layout.minimumInteritemSpacing = 12
self.registerForPreviewing(with: self, sourceView: self.collectionView!)
let longPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(GameCollectionViewController.handleLongPressGesture(_:)))
self.collectionView?.addGestureRecognizer(longPressGestureRecognizer)
if #available(iOS 13, *) {}
else
{
self.registerForPreviewing(with: self, sourceView: self.collectionView!)
let longPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(GameCollectionViewController.handleLongPressGesture(_:)))
self.collectionView?.addGestureRecognizer(longPressGestureRecognizer)
}
}
override func viewWillDisappear(_ animated: Bool)
{
super.viewWillDisappear(animated)
if _performing3DTouchTransition
if _performingPreviewTransition
{
_performing3DTouchTransition = false
_performingPreviewTransition = false
// Unlike our custom transitions, 3D Touch transition doesn't manually call appearance methods for us
// To compensate, we call them ourselves
_destination3DTouchTransitionViewController?.beginAppearanceTransition(true, animated: true)
_previewTransitionDestinationViewController?.beginAppearanceTransition(true, animated: true)
self.transitionCoordinator?.animate(alongsideTransition: nil, completion: { (context) in
self._destination3DTouchTransitionViewController?.endAppearanceTransition()
self._destination3DTouchTransitionViewController = nil
self._previewTransitionDestinationViewController?.endAppearanceTransition()
self._previewTransitionDestinationViewController = nil
})
}
}
@ -176,9 +182,9 @@ extension GameCollectionViewController
self.activeSaveState = nil
if _performing3DTouchTransition
if _performingPreviewTransition
{
_destination3DTouchTransitionViewController = destinationViewController
_previewTransitionDestinationViewController = destinationViewController
}
default: break
@ -380,27 +386,27 @@ private extension GameCollectionViewController
{
let cancelAction = Action(title: NSLocalizedString("Cancel", comment: ""), style: .cancel, action: nil)
let renameAction = Action(title: NSLocalizedString("Rename", comment: ""), style: .default, action: { [unowned self] action in
let renameAction = Action(title: NSLocalizedString("Rename", comment: ""), style: .default, image: UIImage(symbolNameIfAvailable: "pencil.and.ellipsis.rectangle"), action: { [unowned self] action in
self.rename(game)
})
let changeArtworkAction = Action(title: NSLocalizedString("Change Artwork", comment: ""), style: .default) { [unowned self] action in
let changeArtworkAction = Action(title: NSLocalizedString("Change Artwork", comment: ""), style: .default, image: UIImage(symbolNameIfAvailable: "photo")) { [unowned self] action in
self.changeArtwork(for: game)
}
let shareAction = Action(title: NSLocalizedString("Share", comment: ""), style: .default, action: { [unowned self] action in
let shareAction = Action(title: NSLocalizedString("Share", comment: ""), style: .default, image: UIImage(symbolNameIfAvailable: "square.and.arrow.up"), action: { [unowned self] action in
self.share(game)
})
let saveStatesAction = Action(title: NSLocalizedString("Save States", comment: ""), style: .default, action: { [unowned self] action in
let saveStatesAction = Action(title: NSLocalizedString("Save States", comment: ""), style: .default, image: UIImage(symbolNameIfAvailable: "doc.on.doc"), action: { [unowned self] action in
self.viewSaveStates(for: game)
})
let importSaveFile = Action(title: NSLocalizedString("Import Save File", comment: ""), style: .default) { [unowned self] _ in
let importSaveFile = Action(title: NSLocalizedString("Import Save File", comment: ""), style: .default, image: UIImage(symbolNameIfAvailable: "tray.and.arrow.down")) { [unowned self] _ in
self.importSaveFile(for: game)
}
let deleteAction = Action(title: NSLocalizedString("Delete", comment: ""), style: .destructive, action: { [unowned self] action in
let deleteAction = Action(title: NSLocalizedString("Delete", comment: ""), style: .destructive, image: UIImage(symbolNameIfAvailable: "trash"), action: { [unowned self] action in
self.delete(game)
})
@ -687,6 +693,18 @@ extension GameCollectionViewController: UIViewControllerPreviewingDelegate
let game = self.dataSource.item(at: indexPath)
let gameViewController = self.makePreviewGameViewController(for: game)
_previewTransitionViewController = gameViewController
return gameViewController
}
func previewingContext(_ previewingContext: UIViewControllerPreviewing, commit viewControllerToCommit: UIViewController)
{
self.commitPreviewTransition()
}
func makePreviewGameViewController(for game: Game) -> PreviewGameViewController
{
let gameViewController = PreviewGameViewController()
gameViewController.game = game
@ -702,11 +720,11 @@ extension GameCollectionViewController: UIViewControllerPreviewingDelegate
return gameViewController
}
func previewingContext(_ previewingContext: UIViewControllerPreviewing, commit viewControllerToCommit: UIViewController)
func commitPreviewTransition()
{
let gameViewController = viewControllerToCommit as! PreviewGameViewController
let game = gameViewController.game as! Game
guard let gameViewController = _previewTransitionViewController else { return }
let game = gameViewController.game as! Game
gameViewController.pauseEmulation()
let indexPath = self.dataSource.fetchedResultsController.indexPath(forObject: game)!
@ -716,7 +734,7 @@ extension GameCollectionViewController: UIViewControllerPreviewingDelegate
gameViewController.emulatorCore?.stop()
_performing3DTouchTransition = true
_performingPreviewTransition = true
self.launchGame(at: indexPath, clearScreen: true, ignoreAlreadyRunningError: true)
@ -805,3 +823,55 @@ extension GameCollectionViewController: UICollectionViewDelegateFlowLayout
return size
}
}
@available(iOS 13.0, *)
extension GameCollectionViewController
{
override func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration?
{
let game = self.dataSource.item(at: indexPath)
let actions = self.actions(for: game)
return UIContextMenuConfiguration(identifier: indexPath as NSIndexPath, previewProvider: { [weak self] in
guard let self = self else { return nil }
let previewViewController = self.makePreviewGameViewController(for: game)
self._previewTransitionViewController = previewViewController
return previewViewController
}) { suggestedActions in
return UIMenu(title: "", children: actions.menuActions)
}
}
override func collectionView(_ collectionView: UICollectionView, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating)
{
self.commitPreviewTransition()
}
override func collectionView(_ collectionView: UICollectionView, previewForHighlightingContextMenuWithConfiguration configuration: UIContextMenuConfiguration) -> UITargetedPreview?
{
guard let indexPath = configuration.identifier as? NSIndexPath else { return nil }
guard let cell = collectionView.cellForItem(at: indexPath as IndexPath) as? GridCollectionViewCell else { return nil }
let parameters = UIPreviewParameters()
parameters.backgroundColor = .clear
if let image = cell.imageView.image
{
let artworkFrame = AVMakeRect(aspectRatio: image.size, insideRect: cell.imageView.bounds)
let bezierPath = UIBezierPath(rect: artworkFrame)
parameters.visiblePath = bezierPath
}
let preview = UITargetedPreview(view: cell.imageView, parameters: parameters)
return preview
}
override func collectionView(_ collectionView: UICollectionView, previewForDismissingContextMenuWithConfiguration configuration: UIContextMenuConfiguration) -> UITargetedPreview?
{
_previewTransitionViewController = nil
return self.collectionView(collectionView, previewForHighlightingContextMenuWithConfiguration: configuration)
}
}

View File

@ -66,6 +66,8 @@ class SaveStatesViewController: UICollectionViewController
private var prototypeCellWidthConstraint: NSLayoutConstraint!
private var prototypeHeader = SaveStatesCollectionHeaderView()
private weak var _previewTransitionViewController: PreviewGameViewController?
private let dataSource: RSTFetchedResultsCollectionViewPrefetchingDataSource<SaveState, UIImage>
private var emulatorCoreSaveState: SaveStateProtocol?
@ -115,12 +117,16 @@ extension SaveStatesViewController
self.prototypeCellWidthConstraint = self.prototypeCell.contentView.widthAnchor.constraint(equalToConstant: collectionViewLayout.itemWidth)
self.prototypeCellWidthConstraint.isActive = true
let longPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(SaveStatesViewController.handleLongPressGesture(_:)))
self.collectionView?.addGestureRecognizer(longPressGestureRecognizer)
self.prepareEmulatorCoreSaveState()
self.registerForPreviewing(with: self, sourceView: self.collectionView!)
if #available(iOS 13, *) {}
else
{
self.registerForPreviewing(with: self, sourceView: self.collectionView!)
let longPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(SaveStatesViewController.handleLongPressGesture(_:)))
self.collectionView?.addGestureRecognizer(longPressGestureRecognizer)
}
self.navigationController?.navigationBar.barStyle = .blackTranslucent
self.navigationController?.toolbar.barStyle = .blackTranslucent
@ -393,7 +399,18 @@ private extension SaveStatesViewController
func updatePreviewSaveState(_ saveState: SaveState?)
{
let alertController = UIAlertController(title: NSLocalizedString("Change Preview Save State?", comment: ""), message: NSLocalizedString("The Preview Save State is loaded whenever you preview this game from the Main Menu with 3D Touch. Are you sure you want to change it?", comment: ""), preferredStyle: .alert)
let message: String
if #available(iOS 13, *)
{
message = NSLocalizedString("The Preview Save State is loaded whenever you long press this game from the Main Menu. Are you sure you want to change it?", comment: "")
}
else
{
message = NSLocalizedString("The Preview Save State is loaded whenever you 3D Touch this game from the Main Menu. Are you sure you want to change it?", comment: "")
}
let alertController = UIAlertController(title: NSLocalizedString("Change Preview Save State?", comment: ""), message: message, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: NSLocalizedString("Cancel", comment: ""), style: .cancel, handler: nil))
alertController.addAction(UIAlertAction(title: NSLocalizedString("Change", comment: ""), style: .default, handler: { (action) in
@ -471,20 +488,31 @@ private extension SaveStatesViewController
{
guard saveState.type != .auto else { return nil }
let isPreviewAvailable: Bool
if #available(iOS 13, *)
{
isPreviewAvailable = true
}
else
{
isPreviewAvailable = (self.traitCollection.forceTouchCapability == .available)
}
var actions = [Action]()
if self.traitCollection.forceTouchCapability == .available
if isPreviewAvailable
{
if saveState.game?.previewSaveState != saveState
{
let previewAction = Action(title: NSLocalizedString("Set as Preview Save State", comment: ""), style: .default, action: { [unowned self] action in
let previewAction = Action(title: NSLocalizedString("Set as Preview Save State", comment: ""), style: .default, image: UIImage(symbolNameIfAvailable: "eye.fill"), action: { [unowned self] action in
self.updatePreviewSaveState(saveState)
})
actions.append(previewAction)
}
else
{
let previewAction = Action(title: NSLocalizedString("Remove as Preview Save State", comment: ""), style: .default, action: { [unowned self] action in
let previewAction = Action(title: NSLocalizedString("Remove as Preview Save State", comment: ""), style: .default, image: UIImage(symbolNameIfAvailable: "eye.slash.fill"), action: { [unowned self] action in
self.updatePreviewSaveState(nil)
})
actions.append(previewAction)
@ -494,7 +522,7 @@ private extension SaveStatesViewController
let cancelAction = Action(title: NSLocalizedString("Cancel", comment: ""), style: .cancel, action: nil)
actions.append(cancelAction)
let renameAction = Action(title: NSLocalizedString("Rename", comment: ""), style: .default, action: { [unowned self] action in
let renameAction = Action(title: NSLocalizedString("Rename", comment: ""), style: .default, image: UIImage(symbolNameIfAvailable: "pencil.and.ellipsis.rectangle"), action: { [unowned self] action in
self.renameSaveState(saveState)
})
actions.append(renameAction)
@ -504,19 +532,19 @@ private extension SaveStatesViewController
case .auto: break
case .quick: break
case .general:
let lockAction = Action(title: NSLocalizedString("Lock", comment: ""), style: .default, action: { [unowned self] action in
let lockAction = Action(title: NSLocalizedString("Lock", comment: ""), style: .default, image: UIImage(symbolNameIfAvailable: "lock.fill"), action: { [unowned self] action in
self.lockSaveState(saveState)
})
actions.append(lockAction)
case .locked:
let unlockAction = Action(title: NSLocalizedString("Unlock", comment: ""), style: .default, action: { [unowned self] action in
let unlockAction = Action(title: NSLocalizedString("Unlock", comment: ""), style: .default, image: UIImage(symbolNameIfAvailable: "lock.open.fill"), action: { [unowned self] action in
self.unlockSaveState(saveState)
})
actions.append(unlockAction)
}
let deleteAction = Action(title: NSLocalizedString("Delete", comment: ""), style: .destructive, action: { [unowned self] action in
let deleteAction = Action(title: NSLocalizedString("Delete", comment: ""), style: .destructive, image: UIImage(symbolNameIfAvailable: "trash"), action: { [unowned self] action in
self.deleteSaveState(saveState)
})
actions.append(deleteAction)
@ -608,21 +636,36 @@ extension SaveStatesViewController: UIViewControllerPreviewingDelegate
previewingContext.sourceRect = layoutAttributes.frame
let saveState = self.dataSource.item(at: indexPath)
let actions = self.actionsForSaveState(saveState)?.previewActions ?? []
let previewImage = self.dataSource.prefetchItemCache.object(forKey: saveState) ?? UIImage(contentsOfFile: saveState.imageFileURL.path)
let previewGameViewController = PreviewGameViewController()
previewGameViewController.game = self.game
previewGameViewController.overridePreviewActionItems = actions
previewGameViewController.previewSaveState = saveState
previewGameViewController.previewImage = previewImage
let previewGameViewController = self.makePreviewGameViewController(for: saveState)
_previewTransitionViewController = previewGameViewController
return previewGameViewController
}
func previewingContext(_ previewingContext: UIViewControllerPreviewing, commit viewControllerToCommit: UIViewController)
{
let gameViewController = viewControllerToCommit as! PreviewGameViewController
self.commitPreviewTransition()
}
func makePreviewGameViewController(for saveState: SaveState) -> PreviewGameViewController
{
let previewImage = self.dataSource.prefetchItemCache.object(forKey: saveState) ?? UIImage(contentsOfFile: saveState.imageFileURL.path)
let gameViewController = PreviewGameViewController()
gameViewController.game = self.game
gameViewController.previewSaveState = saveState
gameViewController.previewImage = previewImage
let actions = self.actionsForSaveState(saveState)?.previewActions ?? []
gameViewController.overridePreviewActionItems = actions
return gameViewController
}
func commitPreviewTransition()
{
guard let gameViewController = self._previewTransitionViewController else { return }
gameViewController.pauseEmulation()
let fileURL = FileManager.default.uniqueTemporaryURL()
@ -708,3 +751,47 @@ extension SaveStatesViewController: UICollectionViewDelegateFlowLayout
return size
}
}
@available(iOS 13.0, *)
extension SaveStatesViewController
{
override func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration?
{
let saveState = self.dataSource.item(at: indexPath)
guard let actions = self.actionsForSaveState(saveState) else { return nil }
return UIContextMenuConfiguration(identifier: indexPath as NSIndexPath, previewProvider: { [weak self] in
guard let self = self else { return nil }
let previewGameViewController = self.makePreviewGameViewController(for: saveState)
self._previewTransitionViewController = previewGameViewController
return previewGameViewController
}) { suggestedActions in
return UIMenu(title: "", children: actions.menuActions)
}
}
override func collectionView(_ collectionView: UICollectionView, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating)
{
self.commitPreviewTransition()
}
override func collectionView(_ collectionView: UICollectionView, previewForHighlightingContextMenuWithConfiguration configuration: UIContextMenuConfiguration) -> UITargetedPreview?
{
guard let indexPath = configuration.identifier as? NSIndexPath else { return nil }
guard let cell = collectionView.cellForItem(at: indexPath as IndexPath) as? GridCollectionViewCell else { return nil }
let parameters = UIPreviewParameters()
parameters.backgroundColor = .clear
let preview = UITargetedPreview(view: cell.imageView, parameters: parameters)
return preview
}
override func collectionView(_ collectionView: UICollectionView, previewForDismissingContextMenuWithConfiguration configuration: UIContextMenuConfiguration) -> UITargetedPreview?
{
self._previewTransitionViewController = nil
return self.collectionView(collectionView, previewForHighlightingContextMenuWithConfiguration: configuration)
}
}