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"?>
|
||||
<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>
|
||||
<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"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
@ -93,7 +93,7 @@
|
||||
<!--Emulation View Controller-->
|
||||
<scene sceneID="g58-A4-ib1">
|
||||
<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>
|
||||
<viewControllerLayoutGuide type="top" id="Fo4-YI-5cN"/>
|
||||
<viewControllerLayoutGuide type="bottom" id="UIe-oC-tUc"/>
|
||||
|
||||
@ -23,10 +23,23 @@ class EmulationViewController: UIViewController
|
||||
guard oldValue != game else { return }
|
||||
|
||||
self.emulatorCore = SNESEmulatorCore(game: game)
|
||||
|
||||
}
|
||||
}
|
||||
private(set) var emulatorCore: EmulatorCore! {
|
||||
didSet
|
||||
{
|
||||
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
|
||||
@IBOutlet private var controllerView: ControllerView!
|
||||
@ -90,7 +103,14 @@ class EmulationViewController: UIViewController
|
||||
{
|
||||
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()
|
||||
@ -180,12 +200,25 @@ class EmulationViewController: UIViewController
|
||||
self.pauseViewController = nil
|
||||
|
||||
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 -
|
||||
/// 3D Touch
|
||||
override func previewActionItems() -> [UIPreviewActionItem]
|
||||
{
|
||||
if let previewActionItems = self.overridePreviewActionItems
|
||||
{
|
||||
return previewActionItems
|
||||
}
|
||||
|
||||
let presentingViewController = self.presentingViewController
|
||||
|
||||
let launchGameAction = UIPreviewAction(title: NSLocalizedString("Launch \(self.game.name)", comment: ""), style: .Default) { (action, viewController) in
|
||||
@ -227,9 +260,9 @@ private extension EmulationViewController
|
||||
/// Save States
|
||||
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)
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -14,9 +14,9 @@ import Roxas
|
||||
|
||||
protocol SaveStatesViewControllerDelegate: class
|
||||
{
|
||||
func saveStatesViewControllerActiveGame(saveStatesViewController: SaveStatesViewController) -> Game
|
||||
func saveStatesViewControllerActiveEmulatorCore(saveStatesViewController: SaveStatesViewController) -> EmulatorCore
|
||||
func saveStatesViewController(saveStatesViewController: SaveStatesViewController, updateSaveState saveState: SaveState)
|
||||
func saveStatesViewController(saveStatesViewController: SaveStatesViewController, loadSaveState saveState: SaveState)
|
||||
func saveStatesViewController(saveStatesViewController: SaveStatesViewController, loadSaveState saveState: SaveStateType)
|
||||
}
|
||||
|
||||
extension SaveStatesViewController
|
||||
@ -56,6 +56,8 @@ class SaveStatesViewController: UICollectionViewController
|
||||
private let imageOperationQueue = RSTOperationQueue()
|
||||
private let imageCache = NSCache()
|
||||
|
||||
private var currentGameState: SaveStateType?
|
||||
|
||||
private let dateFormatter: NSDateFormatter
|
||||
|
||||
required init?(coder aDecoder: NSCoder)
|
||||
@ -109,6 +111,8 @@ extension SaveStatesViewController
|
||||
let longPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: "handleLongPressGesture:")
|
||||
self.collectionView?.addGestureRecognizer(longPressGestureRecognizer)
|
||||
|
||||
self.registerForPreviewingWithDelegate(self, sourceView: self.collectionView!)
|
||||
|
||||
self.updateBackgroundView()
|
||||
}
|
||||
|
||||
@ -130,6 +134,13 @@ extension SaveStatesViewController
|
||||
|
||||
super.viewWillAppear(animated)
|
||||
}
|
||||
|
||||
override func viewDidDisappear(animated: Bool)
|
||||
{
|
||||
super.viewDidDisappear(animated)
|
||||
|
||||
self.resetEmulatorCoreIfNeeded()
|
||||
}
|
||||
|
||||
override func didReceiveMemoryWarning()
|
||||
{
|
||||
@ -143,7 +154,7 @@ private extension SaveStatesViewController
|
||||
|
||||
func updateFetchedResultsController()
|
||||
{
|
||||
let game = self.delegate.saveStatesViewControllerActiveGame(self)
|
||||
let game = self.delegate.saveStatesViewControllerActiveEmulatorCore(self).game as! Game
|
||||
|
||||
let fetchRequest = SaveState.fetchRequest()
|
||||
fetchRequest.returnsObjectsAsFaults = false
|
||||
@ -264,7 +275,7 @@ private extension SaveStatesViewController
|
||||
let backgroundContext = DatabaseManager.sharedManager.backgroundManagedObjectContext()
|
||||
backgroundContext.performBlock {
|
||||
|
||||
var game = self.delegate.saveStatesViewControllerActiveGame(self)
|
||||
var game = self.delegate.saveStatesViewControllerActiveEmulatorCore(self).game as! Game
|
||||
game = backgroundContext.objectWithID(game.objectID) as! Game
|
||||
|
||||
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 -
|
||||
|
||||
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> -
|
||||
extension SaveStatesViewController
|
||||
{
|
||||
|
||||
Loading…
Reference in New Issue
Block a user