Replaces peek & pop with context menus on iOS 13+
This commit is contained in:
parent
748f930186
commit
cd7e9652ab
@ -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 */,
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
24
Delta/Extensions/UIImage+SymbolFallback.swift
Normal file
24
Delta/Extensions/UIImage+SymbolFallback.swift
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user