Adds ability to Peek & Pop save states via 3D Touch
This commit is contained in:
parent
832fac25b3
commit
c6e2820458
@ -1 +1 @@
|
|||||||
Subproject commit 4406d30cec9e252979a87966bcce5c5d3711b985
|
Subproject commit 29abe10afb7f720e40b9345bc88f321c518efbf6
|
||||||
@ -1 +1 @@
|
|||||||
Subproject commit 015d8b1a7fc364a9887a2bb2d76a9215f7ead19c
|
Subproject commit 87d9cfa5574fe42db453f9b6b08b3e7190a4b7d2
|
||||||
@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="9531" systemVersion="15D21" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="wKV-3d-NIY">
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="9532" systemVersion="15D21" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="wKV-3d-NIY">
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9529"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9530"/>
|
||||||
<capability name="Segues with Peek and Pop" minToolsVersion="7.1"/>
|
<capability name="Segues with Peek and Pop" minToolsVersion="7.1"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<scenes>
|
<scenes>
|
||||||
@ -93,7 +93,7 @@
|
|||||||
<!--Emulation View Controller-->
|
<!--Emulation View Controller-->
|
||||||
<scene sceneID="g58-A4-ib1">
|
<scene sceneID="g58-A4-ib1">
|
||||||
<objects>
|
<objects>
|
||||||
<viewController id="hx4-Ze-0Jw" customClass="EmulationViewController" customModule="Delta" customModuleProvider="target" sceneMemberID="viewController">
|
<viewController storyboardIdentifier="emulationViewController" id="hx4-Ze-0Jw" customClass="EmulationViewController" customModule="Delta" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<layoutGuides>
|
<layoutGuides>
|
||||||
<viewControllerLayoutGuide type="top" id="Fo4-YI-5cN"/>
|
<viewControllerLayoutGuide type="top" id="Fo4-YI-5cN"/>
|
||||||
<viewControllerLayoutGuide type="bottom" id="UIe-oC-tUc"/>
|
<viewControllerLayoutGuide type="bottom" id="UIe-oC-tUc"/>
|
||||||
|
|||||||
@ -23,10 +23,23 @@ class EmulationViewController: UIViewController
|
|||||||
guard oldValue != game else { return }
|
guard oldValue != game else { return }
|
||||||
|
|
||||||
self.emulatorCore = SNESEmulatorCore(game: game)
|
self.emulatorCore = SNESEmulatorCore(game: game)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private(set) var emulatorCore: EmulatorCore! {
|
||||||
|
didSet
|
||||||
|
{
|
||||||
self.preferredContentSize = self.emulatorCore.preferredRenderingSize
|
self.preferredContentSize = self.emulatorCore.preferredRenderingSize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private(set) var emulatorCore: EmulatorCore!
|
|
||||||
|
// If non-nil, will override the default preview action items returned in previewActionItems()
|
||||||
|
var overridePreviewActionItems: [UIPreviewActionItem]?
|
||||||
|
|
||||||
|
// Annoying iOS gotcha: if the previewingContext(_:viewControllerForLocation:) callback takes too long, the peek/preview starts, but fails to actually present the view controller
|
||||||
|
// To workaround, we have this closure to defer work for Peeking/Popping until the view controller appears
|
||||||
|
// Hacky, but works
|
||||||
|
var deferredPreparationHandler: (Void -> Void)?
|
||||||
|
|
||||||
//MARK: - Private Properties
|
//MARK: - Private Properties
|
||||||
@IBOutlet private var controllerView: ControllerView!
|
@IBOutlet private var controllerView: ControllerView!
|
||||||
@ -90,7 +103,14 @@ class EmulationViewController: UIViewController
|
|||||||
{
|
{
|
||||||
super.viewDidAppear(animated)
|
super.viewDidAppear(animated)
|
||||||
|
|
||||||
self.emulatorCore.startEmulation()
|
self.deferredPreparationHandler?()
|
||||||
|
|
||||||
|
switch self.emulatorCore.state
|
||||||
|
{
|
||||||
|
case .Stopped: self.emulatorCore.startEmulation()
|
||||||
|
case .Running: break
|
||||||
|
case .Paused: self.emulatorCore.resumeEmulation()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewDidLayoutSubviews()
|
override func viewDidLayoutSubviews()
|
||||||
@ -180,12 +200,25 @@ class EmulationViewController: UIViewController
|
|||||||
self.pauseViewController = nil
|
self.pauseViewController = nil
|
||||||
|
|
||||||
self.emulatorCore.resumeEmulation()
|
self.emulatorCore.resumeEmulation()
|
||||||
|
|
||||||
|
// Temporarily disable audioManager to prevent delayed audio bug when using 3D Touch Peek & Pop
|
||||||
|
self.emulatorCore.audioManager.enabled = false
|
||||||
|
|
||||||
|
// Re-enable after delay
|
||||||
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(0.1 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) {
|
||||||
|
self.emulatorCore.audioManager.enabled = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//MARK: - 3D Touch -
|
//MARK: - 3D Touch -
|
||||||
/// 3D Touch
|
/// 3D Touch
|
||||||
override func previewActionItems() -> [UIPreviewActionItem]
|
override func previewActionItems() -> [UIPreviewActionItem]
|
||||||
{
|
{
|
||||||
|
if let previewActionItems = self.overridePreviewActionItems
|
||||||
|
{
|
||||||
|
return previewActionItems
|
||||||
|
}
|
||||||
|
|
||||||
let presentingViewController = self.presentingViewController
|
let presentingViewController = self.presentingViewController
|
||||||
|
|
||||||
let launchGameAction = UIPreviewAction(title: NSLocalizedString("Launch \(self.game.name)", comment: ""), style: .Default) { (action, viewController) in
|
let launchGameAction = UIPreviewAction(title: NSLocalizedString("Launch \(self.game.name)", comment: ""), style: .Default) { (action, viewController) in
|
||||||
@ -227,9 +260,9 @@ private extension EmulationViewController
|
|||||||
/// Save States
|
/// Save States
|
||||||
extension EmulationViewController: SaveStatesViewControllerDelegate
|
extension EmulationViewController: SaveStatesViewControllerDelegate
|
||||||
{
|
{
|
||||||
func saveStatesViewControllerActiveGame(saveStatesViewController: SaveStatesViewController) -> Game
|
func saveStatesViewControllerActiveEmulatorCore(saveStatesViewController: SaveStatesViewController) -> EmulatorCore
|
||||||
{
|
{
|
||||||
return self.game
|
return self.emulatorCore
|
||||||
}
|
}
|
||||||
|
|
||||||
func saveStatesViewController(saveStatesViewController: SaveStatesViewController, updateSaveState saveState: SaveState)
|
func saveStatesViewController(saveStatesViewController: SaveStatesViewController, updateSaveState saveState: SaveState)
|
||||||
@ -276,7 +309,7 @@ extension EmulationViewController: SaveStatesViewControllerDelegate
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func saveStatesViewController(saveStatesViewController: SaveStatesViewController, loadSaveState saveState: SaveState)
|
func saveStatesViewController(saveStatesViewController: SaveStatesViewController, loadSaveState saveState: SaveStateType)
|
||||||
{
|
{
|
||||||
self.emulatorCore.loadSaveState(saveState)
|
self.emulatorCore.loadSaveState(saveState)
|
||||||
|
|
||||||
|
|||||||
@ -14,9 +14,9 @@ import Roxas
|
|||||||
|
|
||||||
protocol SaveStatesViewControllerDelegate: class
|
protocol SaveStatesViewControllerDelegate: class
|
||||||
{
|
{
|
||||||
func saveStatesViewControllerActiveGame(saveStatesViewController: SaveStatesViewController) -> Game
|
func saveStatesViewControllerActiveEmulatorCore(saveStatesViewController: SaveStatesViewController) -> EmulatorCore
|
||||||
func saveStatesViewController(saveStatesViewController: SaveStatesViewController, updateSaveState saveState: SaveState)
|
func saveStatesViewController(saveStatesViewController: SaveStatesViewController, updateSaveState saveState: SaveState)
|
||||||
func saveStatesViewController(saveStatesViewController: SaveStatesViewController, loadSaveState saveState: SaveState)
|
func saveStatesViewController(saveStatesViewController: SaveStatesViewController, loadSaveState saveState: SaveStateType)
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SaveStatesViewController
|
extension SaveStatesViewController
|
||||||
@ -56,6 +56,8 @@ class SaveStatesViewController: UICollectionViewController
|
|||||||
private let imageOperationQueue = RSTOperationQueue()
|
private let imageOperationQueue = RSTOperationQueue()
|
||||||
private let imageCache = NSCache()
|
private let imageCache = NSCache()
|
||||||
|
|
||||||
|
private var currentGameState: SaveStateType?
|
||||||
|
|
||||||
private let dateFormatter: NSDateFormatter
|
private let dateFormatter: NSDateFormatter
|
||||||
|
|
||||||
required init?(coder aDecoder: NSCoder)
|
required init?(coder aDecoder: NSCoder)
|
||||||
@ -109,6 +111,8 @@ extension SaveStatesViewController
|
|||||||
let longPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: "handleLongPressGesture:")
|
let longPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: "handleLongPressGesture:")
|
||||||
self.collectionView?.addGestureRecognizer(longPressGestureRecognizer)
|
self.collectionView?.addGestureRecognizer(longPressGestureRecognizer)
|
||||||
|
|
||||||
|
self.registerForPreviewingWithDelegate(self, sourceView: self.collectionView!)
|
||||||
|
|
||||||
self.updateBackgroundView()
|
self.updateBackgroundView()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,6 +134,13 @@ extension SaveStatesViewController
|
|||||||
|
|
||||||
super.viewWillAppear(animated)
|
super.viewWillAppear(animated)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override func viewDidDisappear(animated: Bool)
|
||||||
|
{
|
||||||
|
super.viewDidDisappear(animated)
|
||||||
|
|
||||||
|
self.resetEmulatorCoreIfNeeded()
|
||||||
|
}
|
||||||
|
|
||||||
override func didReceiveMemoryWarning()
|
override func didReceiveMemoryWarning()
|
||||||
{
|
{
|
||||||
@ -143,7 +154,7 @@ private extension SaveStatesViewController
|
|||||||
|
|
||||||
func updateFetchedResultsController()
|
func updateFetchedResultsController()
|
||||||
{
|
{
|
||||||
let game = self.delegate.saveStatesViewControllerActiveGame(self)
|
let game = self.delegate.saveStatesViewControllerActiveEmulatorCore(self).game as! Game
|
||||||
|
|
||||||
let fetchRequest = SaveState.fetchRequest()
|
let fetchRequest = SaveState.fetchRequest()
|
||||||
fetchRequest.returnsObjectsAsFaults = false
|
fetchRequest.returnsObjectsAsFaults = false
|
||||||
@ -264,7 +275,7 @@ private extension SaveStatesViewController
|
|||||||
let backgroundContext = DatabaseManager.sharedManager.backgroundManagedObjectContext()
|
let backgroundContext = DatabaseManager.sharedManager.backgroundManagedObjectContext()
|
||||||
backgroundContext.performBlock {
|
backgroundContext.performBlock {
|
||||||
|
|
||||||
var game = self.delegate.saveStatesViewControllerActiveGame(self)
|
var game = self.delegate.saveStatesViewControllerActiveEmulatorCore(self).game as! Game
|
||||||
game = backgroundContext.objectWithID(game.objectID) as! Game
|
game = backgroundContext.objectWithID(game.objectID) as! Game
|
||||||
|
|
||||||
let saveState = SaveState.insertIntoManagedObjectContext(backgroundContext)
|
let saveState = SaveState.insertIntoManagedObjectContext(backgroundContext)
|
||||||
@ -324,6 +335,39 @@ private extension SaveStatesViewController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//MARK: - Emulator -
|
||||||
|
|
||||||
|
func resetEmulatorCoreIfNeeded()
|
||||||
|
{
|
||||||
|
// Kinda hacky, but isMovingFromParentViewController only returns yes when popping off navigation controller, and not being dismissed modally
|
||||||
|
// Because of this, this is only run when the user returns to PauseMenuViewController, and not when they choose a save state to load
|
||||||
|
guard let saveState = self.currentGameState where self.isMovingFromParentViewController() else { return }
|
||||||
|
|
||||||
|
// We stopped emulation for 3D Touch, so now we must resume emulation and load the save state we made to make it seem like it was never stopped
|
||||||
|
let emulatorCore = self.delegate.saveStatesViewControllerActiveEmulatorCore(self)
|
||||||
|
|
||||||
|
// Temporarily disable video rendering to prevent flickers
|
||||||
|
emulatorCore.videoManager.enabled = false
|
||||||
|
|
||||||
|
// Load the save state we stored a reference to
|
||||||
|
emulatorCore.startEmulation()
|
||||||
|
emulatorCore.pauseEmulation()
|
||||||
|
emulatorCore.loadSaveState(saveState)
|
||||||
|
|
||||||
|
// Re-enable video rendering
|
||||||
|
emulatorCore.videoManager.enabled = true
|
||||||
|
|
||||||
|
// Remove temporary save state file
|
||||||
|
do
|
||||||
|
{
|
||||||
|
try NSFileManager.defaultManager().removeItemAtURL(saveState.fileURL)
|
||||||
|
}
|
||||||
|
catch let error as NSError
|
||||||
|
{
|
||||||
|
print(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//MARK: - Convenience Methods -
|
//MARK: - Convenience Methods -
|
||||||
|
|
||||||
func correctedSectionForSectionIndex(section: Int) -> Section
|
func correctedSectionForSectionIndex(section: Int) -> Section
|
||||||
@ -336,6 +380,79 @@ private extension SaveStatesViewController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//MARK: - <UIViewControllerPreviewingDelegate> -
|
||||||
|
extension SaveStatesViewController: UIViewControllerPreviewingDelegate
|
||||||
|
{
|
||||||
|
func previewingContext(previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController?
|
||||||
|
{
|
||||||
|
guard let indexPath = self.collectionView?.indexPathForItemAtPoint(location), layoutAttributes = self.collectionViewLayout.layoutAttributesForItemAtIndexPath(indexPath) else { return nil }
|
||||||
|
|
||||||
|
previewingContext.sourceRect = layoutAttributes.frame
|
||||||
|
|
||||||
|
let emulatorCore = self.delegate.saveStatesViewControllerActiveEmulatorCore(self)
|
||||||
|
let storyboard = UIStoryboard(name: "Main", bundle: nil)
|
||||||
|
|
||||||
|
let emulationViewController = storyboard.instantiateViewControllerWithIdentifier("emulationViewController") as! EmulationViewController
|
||||||
|
emulationViewController.game = emulatorCore.game as! Game
|
||||||
|
emulationViewController.overridePreviewActionItems = []
|
||||||
|
emulationViewController.deferredPreparationHandler = { [unowned emulationViewController] in
|
||||||
|
|
||||||
|
// Store reference to current game state before we stop emulation so we can resume it if user decides to not load a save state
|
||||||
|
if self.currentGameState == nil
|
||||||
|
{
|
||||||
|
emulatorCore.saveSaveState() { saveState in
|
||||||
|
|
||||||
|
let fileURL = NSFileManager.uniqueTemporaryURL()
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
try NSFileManager.defaultManager().moveItemAtURL(saveState.fileURL, toURL: fileURL)
|
||||||
|
}
|
||||||
|
catch let error as NSError
|
||||||
|
{
|
||||||
|
print(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.currentGameState = DeltaCore.SaveState(name: nil, fileURL: fileURL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
emulatorCore.stopEmulation()
|
||||||
|
|
||||||
|
|
||||||
|
let saveState = self.fetchedResultsController.objectAtIndexPath(indexPath) as! SaveState
|
||||||
|
|
||||||
|
emulationViewController.emulatorCore.startEmulation()
|
||||||
|
emulationViewController.emulatorCore.pauseEmulation()
|
||||||
|
emulationViewController.emulatorCore.loadSaveState(saveState)
|
||||||
|
}
|
||||||
|
|
||||||
|
return emulationViewController
|
||||||
|
}
|
||||||
|
|
||||||
|
func previewingContext(previewingContext: UIViewControllerPreviewing, commitViewController viewControllerToCommit: UIViewController)
|
||||||
|
{
|
||||||
|
let emulationViewController = viewControllerToCommit as! EmulationViewController
|
||||||
|
|
||||||
|
emulationViewController.emulatorCore.pauseEmulation()
|
||||||
|
emulationViewController.emulatorCore.saveSaveState() { saveState in
|
||||||
|
|
||||||
|
emulationViewController.emulatorCore.stopEmulation()
|
||||||
|
|
||||||
|
let emulatorCore = self.delegate.saveStatesViewControllerActiveEmulatorCore(self)
|
||||||
|
|
||||||
|
emulatorCore.audioManager.stop()
|
||||||
|
|
||||||
|
emulatorCore.startEmulation()
|
||||||
|
emulatorCore.pauseEmulation()
|
||||||
|
|
||||||
|
self.delegate.saveStatesViewController(self, loadSaveState: saveState)
|
||||||
|
|
||||||
|
emulatorCore.videoManager.enabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//MARK: - <UICollectionViewDataSource> -
|
//MARK: - <UICollectionViewDataSource> -
|
||||||
extension SaveStatesViewController
|
extension SaveStatesViewController
|
||||||
{
|
{
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user