Adds basic support for saving + loading save states
Stores SaveState model objects with Core Data, and displays the available save states in SaveStatesViewController. Currently, tapping + button creates a new save state, while tapping a collection view cell loads the save state, regardless of whether Save State or Load State was selected from the pause menu.
This commit is contained in:
parent
544f1a1801
commit
bf70df6611
@ -21,50 +21,6 @@ class DatabaseManager
|
|||||||
|
|
||||||
let managedObjectContext: NSManagedObjectContext
|
let managedObjectContext: NSManagedObjectContext
|
||||||
|
|
||||||
class var databaseDirectoryURL: NSURL
|
|
||||||
{
|
|
||||||
let documentsDirectoryURL: NSURL
|
|
||||||
|
|
||||||
if UIDevice.currentDevice().userInterfaceIdiom == .TV
|
|
||||||
{
|
|
||||||
documentsDirectoryURL = NSFileManager.defaultManager().URLsForDirectory(NSSearchPathDirectory.CachesDirectory, inDomains: NSSearchPathDomainMask.UserDomainMask).first!
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
documentsDirectoryURL = NSFileManager.defaultManager().URLsForDirectory(NSSearchPathDirectory.DocumentDirectory, inDomains: NSSearchPathDomainMask.UserDomainMask).first!
|
|
||||||
}
|
|
||||||
|
|
||||||
let databaseDirectoryURL = documentsDirectoryURL.URLByAppendingPathComponent("Database")
|
|
||||||
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
try NSFileManager.defaultManager().createDirectoryAtURL(databaseDirectoryURL, withIntermediateDirectories: true, attributes: nil)
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
print(error)
|
|
||||||
}
|
|
||||||
|
|
||||||
return databaseDirectoryURL
|
|
||||||
}
|
|
||||||
|
|
||||||
class var gamesDirectoryURL: NSURL
|
|
||||||
{
|
|
||||||
let gamesDirectoryURL = DatabaseManager.databaseDirectoryURL.URLByAppendingPathComponent("Games")
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
try NSFileManager.defaultManager().createDirectoryAtURL(gamesDirectoryURL, withIntermediateDirectories: true, attributes: nil)
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
print(error)
|
|
||||||
}
|
|
||||||
|
|
||||||
return gamesDirectoryURL
|
|
||||||
}
|
|
||||||
|
|
||||||
private let privateManagedObjectContext: NSManagedObjectContext
|
private let privateManagedObjectContext: NSManagedObjectContext
|
||||||
private let validationManagedObjectContext: NSManagedObjectContext
|
private let validationManagedObjectContext: NSManagedObjectContext
|
||||||
|
|
||||||
@ -228,6 +184,52 @@ class DatabaseManager
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension DatabaseManager
|
||||||
|
{
|
||||||
|
class var databaseDirectoryURL: NSURL
|
||||||
|
{
|
||||||
|
let documentsDirectoryURL: NSURL
|
||||||
|
|
||||||
|
if UIDevice.currentDevice().userInterfaceIdiom == .TV
|
||||||
|
{
|
||||||
|
documentsDirectoryURL = NSFileManager.defaultManager().URLsForDirectory(NSSearchPathDirectory.CachesDirectory, inDomains: NSSearchPathDomainMask.UserDomainMask).first!
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
documentsDirectoryURL = NSFileManager.defaultManager().URLsForDirectory(NSSearchPathDirectory.DocumentDirectory, inDomains: NSSearchPathDomainMask.UserDomainMask).first!
|
||||||
|
}
|
||||||
|
|
||||||
|
let databaseDirectoryURL = documentsDirectoryURL.URLByAppendingPathComponent("Database")
|
||||||
|
self.createDirectoryAtURLIfNeeded(databaseDirectoryURL)
|
||||||
|
|
||||||
|
return databaseDirectoryURL
|
||||||
|
}
|
||||||
|
|
||||||
|
class var gamesDirectoryURL: NSURL
|
||||||
|
{
|
||||||
|
let gamesDirectoryURL = DatabaseManager.databaseDirectoryURL.URLByAppendingPathComponent("Games")
|
||||||
|
self.createDirectoryAtURLIfNeeded(gamesDirectoryURL)
|
||||||
|
|
||||||
|
return gamesDirectoryURL
|
||||||
|
}
|
||||||
|
|
||||||
|
class var saveStatesDirectoryURL: NSURL
|
||||||
|
{
|
||||||
|
let saveStatesDirectoryURL = DatabaseManager.databaseDirectoryURL.URLByAppendingPathComponent("Save States")
|
||||||
|
self.createDirectoryAtURLIfNeeded(saveStatesDirectoryURL)
|
||||||
|
|
||||||
|
return saveStatesDirectoryURL
|
||||||
|
}
|
||||||
|
|
||||||
|
class func saveStatesDirectoryURLForGame(game: Game) -> NSURL
|
||||||
|
{
|
||||||
|
let gameDirectoryURL = DatabaseManager.saveStatesDirectoryURL.URLByAppendingPathComponent(game.identifier)
|
||||||
|
self.createDirectoryAtURLIfNeeded(gameDirectoryURL)
|
||||||
|
|
||||||
|
return gameDirectoryURL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private extension DatabaseManager
|
private extension DatabaseManager
|
||||||
{
|
{
|
||||||
// MARK: - Saving -
|
// MARK: - Saving -
|
||||||
@ -324,4 +326,18 @@ private extension DatabaseManager
|
|||||||
self.save()
|
self.save()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - File Management -
|
||||||
|
|
||||||
|
class func createDirectoryAtURLIfNeeded(URL: NSURL)
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
try NSFileManager.defaultManager().createDirectoryAtURL(URL, withIntermediateDirectories: true, attributes: nil)
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
print(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,8 +20,8 @@ enum GameCollectionAttributes: String
|
|||||||
case games
|
case games
|
||||||
}
|
}
|
||||||
|
|
||||||
extension GameCollection {
|
extension GameCollection
|
||||||
|
{
|
||||||
@NSManaged var identifier: String
|
@NSManaged var identifier: String
|
||||||
@NSManaged var index: Int16
|
@NSManaged var index: Int16
|
||||||
|
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
<model userDefinedModelVersionIdentifier="1.0" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="9057" systemVersion="15B42" minimumToolsVersion="Xcode 7.0">
|
<model userDefinedModelVersionIdentifier="1.0" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="9525" systemVersion="15D21" minimumToolsVersion="Xcode 7.0">
|
||||||
<entity name="Game" representedClassName="Game" syncable="YES">
|
<entity name="Game" representedClassName="Game" syncable="YES">
|
||||||
<attribute name="artworkURL" optional="YES" attributeType="Transformable" syncable="YES">
|
<attribute name="artworkURL" optional="YES" attributeType="Transformable" syncable="YES">
|
||||||
<userInfo>
|
<userInfo>
|
||||||
<entry key="attributeValueClassName" value="NSURL"/>
|
<entry key="attributeValueClassName" value="NSURL"/>
|
||||||
</userInfo>
|
</userInfo>
|
||||||
</attribute>
|
</attribute>
|
||||||
<attribute name="filename" optional="YES" attributeType="String" syncable="YES">
|
<attribute name="filename" attributeType="String" syncable="YES">
|
||||||
<userInfo>
|
<userInfo>
|
||||||
<entry key="attributeValueClassName" value="NSURL"/>
|
<entry key="attributeValueClassName" value="NSURL"/>
|
||||||
</userInfo>
|
</userInfo>
|
||||||
@ -15,6 +15,7 @@
|
|||||||
<attribute name="name" attributeType="String" syncable="YES"/>
|
<attribute name="name" attributeType="String" syncable="YES"/>
|
||||||
<attribute name="typeIdentifier" attributeType="String" syncable="YES"/>
|
<attribute name="typeIdentifier" attributeType="String" syncable="YES"/>
|
||||||
<relationship name="gameCollections" toMany="YES" deletionRule="Nullify" destinationEntity="GameCollection" inverseName="games" inverseEntity="GameCollection" syncable="YES"/>
|
<relationship name="gameCollections" toMany="YES" deletionRule="Nullify" destinationEntity="GameCollection" inverseName="games" inverseEntity="GameCollection" syncable="YES"/>
|
||||||
|
<relationship name="saveStates" toMany="YES" deletionRule="Cascade" destinationEntity="SaveState" inverseName="game" inverseEntity="SaveState" syncable="YES"/>
|
||||||
<uniquenessConstraints>
|
<uniquenessConstraints>
|
||||||
<uniquenessConstraint>
|
<uniquenessConstraint>
|
||||||
<constraint value="identifier"/>
|
<constraint value="identifier"/>
|
||||||
@ -31,8 +32,26 @@
|
|||||||
</uniquenessConstraint>
|
</uniquenessConstraint>
|
||||||
</uniquenessConstraints>
|
</uniquenessConstraints>
|
||||||
</entity>
|
</entity>
|
||||||
|
<entity name="SaveState" representedClassName="SaveState" syncable="YES">
|
||||||
|
<attribute name="creationDate" attributeType="Date" syncable="YES"/>
|
||||||
|
<attribute name="filename" attributeType="String" syncable="YES">
|
||||||
|
<userInfo>
|
||||||
|
<entry key="attributeValueClassName" value="NSURL"/>
|
||||||
|
</userInfo>
|
||||||
|
</attribute>
|
||||||
|
<attribute name="identifier" attributeType="String" syncable="YES"/>
|
||||||
|
<attribute name="modifiedDate" attributeType="Date" syncable="YES"/>
|
||||||
|
<attribute name="name" optional="YES" attributeType="String" syncable="YES"/>
|
||||||
|
<relationship name="game" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Game" inverseName="saveStates" inverseEntity="Game" syncable="YES"/>
|
||||||
|
<uniquenessConstraints>
|
||||||
|
<uniquenessConstraint>
|
||||||
|
<constraint value="identifier"/>
|
||||||
|
</uniquenessConstraint>
|
||||||
|
</uniquenessConstraints>
|
||||||
|
</entity>
|
||||||
<elements>
|
<elements>
|
||||||
<element name="Game" positionX="-200" positionY="9" width="128" height="135"/>
|
<element name="Game" positionX="-200" positionY="-72" width="128" height="148"/>
|
||||||
<element name="GameCollection" positionX="-198" positionY="-207" width="128" height="90"/>
|
<element name="GameCollection" positionX="-198" positionY="-207" width="128" height="90"/>
|
||||||
|
<element name="SaveState" positionX="-198" positionY="113" width="128" height="135"/>
|
||||||
</elements>
|
</elements>
|
||||||
</model>
|
</model>
|
||||||
37
Common/Database/Model/SaveState+CoreDataProperties.swift
Normal file
37
Common/Database/Model/SaveState+CoreDataProperties.swift
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
//
|
||||||
|
// SaveState+CoreDataProperties.swift
|
||||||
|
// Delta
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 1/31/16.
|
||||||
|
// Copyright © 2016 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
// Choose "Create NSManagedObject Subclass…" from the Core Data editor menu
|
||||||
|
// to delete and recreate this implementation file for your updated model.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import CoreData
|
||||||
|
|
||||||
|
enum SaveStateAttributes: String
|
||||||
|
{
|
||||||
|
case filename
|
||||||
|
case identifier
|
||||||
|
case name
|
||||||
|
case creationDate
|
||||||
|
case modifiedDate
|
||||||
|
|
||||||
|
case game
|
||||||
|
}
|
||||||
|
|
||||||
|
extension SaveState
|
||||||
|
{
|
||||||
|
@NSManaged var filename: String
|
||||||
|
@NSManaged var identifier: String
|
||||||
|
@NSManaged var name: String?
|
||||||
|
@NSManaged var creationDate: NSDate
|
||||||
|
@NSManaged var modifiedDate: NSDate
|
||||||
|
|
||||||
|
// Must be optional relationship to satisfy weird Core Data requirement
|
||||||
|
// https://forums.developer.apple.com/thread/20535
|
||||||
|
@NSManaged var game: Game!
|
||||||
|
}
|
||||||
21
Common/Database/Model/SaveState.swift
Normal file
21
Common/Database/Model/SaveState.swift
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
//
|
||||||
|
// SaveState.swift
|
||||||
|
// Delta
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 1/31/16.
|
||||||
|
// Copyright © 2016 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import CoreData
|
||||||
|
|
||||||
|
import DeltaCore
|
||||||
|
|
||||||
|
@objc(SaveState)
|
||||||
|
class SaveState: NSManagedObject, SaveStateType
|
||||||
|
{
|
||||||
|
var fileURL: NSURL {
|
||||||
|
let fileURL = DatabaseManager.saveStatesDirectoryURLForGame(self.game).URLByAppendingPathComponent(self.filename)
|
||||||
|
return fileURL
|
||||||
|
}
|
||||||
|
}
|
||||||
26
Common/Extensions/NSManagedObjectContext+Conveniences.swift
Normal file
26
Common/Extensions/NSManagedObjectContext+Conveniences.swift
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
//
|
||||||
|
// NSManagedObjectContext+Conveniences.swift
|
||||||
|
// Delta
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 2/8/16.
|
||||||
|
// Copyright © 2016 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import CoreData
|
||||||
|
|
||||||
|
extension NSManagedObjectContext
|
||||||
|
{
|
||||||
|
// MARK: - Saving -
|
||||||
|
|
||||||
|
func saveWithErrorLogging()
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
try self.save()
|
||||||
|
}
|
||||||
|
catch let error as NSError
|
||||||
|
{
|
||||||
|
print("Error saving NSManagedObjectContext: ", error, error.userInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1 +1 @@
|
|||||||
Subproject commit d5b6ac78401dc1c9bab50bbf636f42b1c0e9fe7c
|
Subproject commit 23ce4c9b10760ad88d5c4c6703e88f6868a400df
|
||||||
@ -1 +1 @@
|
|||||||
Subproject commit f4e27814258993234883fbca040ea66dc1d9609e
|
Subproject commit 6d9dbe986cdacd0cf2602035608188bcdf2c178a
|
||||||
@ -10,6 +10,12 @@
|
|||||||
AF0535CD7331785FA15E0864 /* Pods.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 22506DA00971C4300AF90A35 /* Pods.framework */; };
|
AF0535CD7331785FA15E0864 /* Pods.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 22506DA00971C4300AF90A35 /* Pods.framework */; };
|
||||||
BF090CF41B490D8300DCAB45 /* UIDevice+Vibration.m in Sources */ = {isa = PBXBuildFile; fileRef = BF090CF31B490D8300DCAB45 /* UIDevice+Vibration.m */; };
|
BF090CF41B490D8300DCAB45 /* UIDevice+Vibration.m in Sources */ = {isa = PBXBuildFile; fileRef = BF090CF31B490D8300DCAB45 /* UIDevice+Vibration.m */; };
|
||||||
BF107EC41BF413F000E0C32C /* GamesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF107EC31BF413F000E0C32C /* GamesViewController.swift */; };
|
BF107EC41BF413F000E0C32C /* GamesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF107EC31BF413F000E0C32C /* GamesViewController.swift */; };
|
||||||
|
BF172AEB1C68986300C26774 /* NSManagedObjectContext+Conveniences.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF172AEA1C68986300C26774 /* NSManagedObjectContext+Conveniences.swift */; };
|
||||||
|
BF172AEC1C68986300C26774 /* NSManagedObjectContext+Conveniences.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF172AEA1C68986300C26774 /* NSManagedObjectContext+Conveniences.swift */; };
|
||||||
|
BF1FB1841C5EE643007E2494 /* SaveState+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1FB1821C5EE643007E2494 /* SaveState+CoreDataProperties.swift */; };
|
||||||
|
BF1FB1851C5EE643007E2494 /* SaveState+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1FB1821C5EE643007E2494 /* SaveState+CoreDataProperties.swift */; };
|
||||||
|
BF1FB1861C5EE643007E2494 /* SaveState.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1FB1831C5EE643007E2494 /* SaveState.swift */; };
|
||||||
|
BF1FB1871C5EE643007E2494 /* SaveState.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1FB1831C5EE643007E2494 /* SaveState.swift */; };
|
||||||
BF27CC8B1BC9FE4D00A20D89 /* Pods.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF27CC8A1BC9FE4D00A20D89 /* Pods.framework */; };
|
BF27CC8B1BC9FE4D00A20D89 /* Pods.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF27CC8A1BC9FE4D00A20D89 /* Pods.framework */; };
|
||||||
BF27CC8C1BC9FE5300A20D89 /* Pods.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 22506DA00971C4300AF90A35 /* Pods.framework */; };
|
BF27CC8C1BC9FE5300A20D89 /* Pods.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 22506DA00971C4300AF90A35 /* Pods.framework */; };
|
||||||
BF27CC8D1BC9FE5300A20D89 /* Pods.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 22506DA00971C4300AF90A35 /* Pods.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
BF27CC8D1BC9FE5300A20D89 /* Pods.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 22506DA00971C4300AF90A35 /* Pods.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||||
@ -127,6 +133,9 @@
|
|||||||
BF090CF21B490D8300DCAB45 /* UIDevice+Vibration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIDevice+Vibration.h"; sourceTree = "<group>"; };
|
BF090CF21B490D8300DCAB45 /* UIDevice+Vibration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIDevice+Vibration.h"; sourceTree = "<group>"; };
|
||||||
BF090CF31B490D8300DCAB45 /* UIDevice+Vibration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIDevice+Vibration.m"; sourceTree = "<group>"; };
|
BF090CF31B490D8300DCAB45 /* UIDevice+Vibration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIDevice+Vibration.m"; sourceTree = "<group>"; };
|
||||||
BF107EC31BF413F000E0C32C /* GamesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GamesViewController.swift; sourceTree = "<group>"; };
|
BF107EC31BF413F000E0C32C /* GamesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GamesViewController.swift; sourceTree = "<group>"; };
|
||||||
|
BF172AEA1C68986300C26774 /* NSManagedObjectContext+Conveniences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSManagedObjectContext+Conveniences.swift"; sourceTree = "<group>"; };
|
||||||
|
BF1FB1821C5EE643007E2494 /* SaveState+CoreDataProperties.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SaveState+CoreDataProperties.swift"; sourceTree = "<group>"; };
|
||||||
|
BF1FB1831C5EE643007E2494 /* SaveState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SaveState.swift; sourceTree = "<group>"; };
|
||||||
BF27CC861BC9E3C600A20D89 /* Delta.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = Delta.entitlements; sourceTree = "<group>"; };
|
BF27CC861BC9E3C600A20D89 /* Delta.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = Delta.entitlements; sourceTree = "<group>"; };
|
||||||
BF27CC8A1BC9FE4D00A20D89 /* Pods.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Pods.framework; path = "Pods/../build/Debug-appletvos/Pods.framework"; sourceTree = "<group>"; };
|
BF27CC8A1BC9FE4D00A20D89 /* Pods.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Pods.framework; path = "Pods/../build/Debug-appletvos/Pods.framework"; sourceTree = "<group>"; };
|
||||||
BF27CC901BCB156200A20D89 /* EmulationViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmulationViewController.swift; sourceTree = "<group>"; };
|
BF27CC901BCB156200A20D89 /* EmulationViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmulationViewController.swift; sourceTree = "<group>"; };
|
||||||
@ -273,6 +282,8 @@
|
|||||||
BFDE39381BC0CEDF003F72E8 /* Game+CoreDataProperties.swift */,
|
BFDE39381BC0CEDF003F72E8 /* Game+CoreDataProperties.swift */,
|
||||||
BFC273171BE6152200D22B05 /* GameCollection.swift */,
|
BFC273171BE6152200D22B05 /* GameCollection.swift */,
|
||||||
BFC273161BE6152200D22B05 /* GameCollection+CoreDataProperties.swift */,
|
BFC273161BE6152200D22B05 /* GameCollection+CoreDataProperties.swift */,
|
||||||
|
BF1FB1831C5EE643007E2494 /* SaveState.swift */,
|
||||||
|
BF1FB1821C5EE643007E2494 /* SaveState+CoreDataProperties.swift */,
|
||||||
);
|
);
|
||||||
path = Model;
|
path = Model;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -302,6 +313,7 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
BF762EAA1BC1B076002C8866 /* NSManagedObject+Conveniences.swift */,
|
BF762EAA1BC1B076002C8866 /* NSManagedObject+Conveniences.swift */,
|
||||||
|
BF172AEA1C68986300C26774 /* NSManagedObjectContext+Conveniences.swift */,
|
||||||
);
|
);
|
||||||
path = Extensions;
|
path = Extensions;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -673,7 +685,9 @@
|
|||||||
BFC273191BE6152200D22B05 /* GameCollection+CoreDataProperties.swift in Sources */,
|
BFC273191BE6152200D22B05 /* GameCollection+CoreDataProperties.swift in Sources */,
|
||||||
BF6BB2411BB73FE800CCF94A /* GameSelectionViewController.swift in Sources */,
|
BF6BB2411BB73FE800CCF94A /* GameSelectionViewController.swift in Sources */,
|
||||||
BF27CC8F1BCA010200A20D89 /* GamePickerController.swift in Sources */,
|
BF27CC8F1BCA010200A20D89 /* GamePickerController.swift in Sources */,
|
||||||
|
BF172AEC1C68986300C26774 /* NSManagedObjectContext+Conveniences.swift in Sources */,
|
||||||
BF7AE81F1C2E984300B1B5BC /* GridCollectionViewCell.swift in Sources */,
|
BF7AE81F1C2E984300B1B5BC /* GridCollectionViewCell.swift in Sources */,
|
||||||
|
BF1FB1851C5EE643007E2494 /* SaveState+CoreDataProperties.swift in Sources */,
|
||||||
BF7AE8251C2E984300B1B5BC /* GridCollectionViewLayout.swift in Sources */,
|
BF7AE8251C2E984300B1B5BC /* GridCollectionViewLayout.swift in Sources */,
|
||||||
BFDE393D1BC0CEDF003F72E8 /* Game.swift in Sources */,
|
BFDE393D1BC0CEDF003F72E8 /* Game.swift in Sources */,
|
||||||
BFC2731B1BE6152200D22B05 /* GameCollection.swift in Sources */,
|
BFC2731B1BE6152200D22B05 /* GameCollection.swift in Sources */,
|
||||||
@ -681,6 +695,7 @@
|
|||||||
BFDE393B1BC0CEDF003F72E8 /* Game+CoreDataProperties.swift in Sources */,
|
BFDE393B1BC0CEDF003F72E8 /* Game+CoreDataProperties.swift in Sources */,
|
||||||
BF27CC911BCB156200A20D89 /* EmulationViewController.swift in Sources */,
|
BF27CC911BCB156200A20D89 /* EmulationViewController.swift in Sources */,
|
||||||
BFB141191BE46934004FBF46 /* GameCollectionViewDataSource.swift in Sources */,
|
BFB141191BE46934004FBF46 /* GameCollectionViewDataSource.swift in Sources */,
|
||||||
|
BF1FB1871C5EE643007E2494 /* SaveState.swift in Sources */,
|
||||||
BFF1E5651BE04CAF000E9EF6 /* BoxArtImageView.swift in Sources */,
|
BFF1E5651BE04CAF000E9EF6 /* BoxArtImageView.swift in Sources */,
|
||||||
BF353FFA1C5D870B00C1184C /* PauseItem.swift in Sources */,
|
BF353FFA1C5D870B00C1184C /* PauseItem.swift in Sources */,
|
||||||
BF3540061C5DA70400C1184C /* SaveStatesViewController.swift in Sources */,
|
BF3540061C5DA70400C1184C /* SaveStatesViewController.swift in Sources */,
|
||||||
@ -699,7 +714,9 @@
|
|||||||
BF3540081C5DAFAD00C1184C /* PauseTransitionCoordinator.swift in Sources */,
|
BF3540081C5DAFAD00C1184C /* PauseTransitionCoordinator.swift in Sources */,
|
||||||
BFAA1FED1B8AA4FA00495943 /* Settings.swift in Sources */,
|
BFAA1FED1B8AA4FA00495943 /* Settings.swift in Sources */,
|
||||||
BF7AE8241C2E984300B1B5BC /* GridCollectionViewLayout.swift in Sources */,
|
BF7AE8241C2E984300B1B5BC /* GridCollectionViewLayout.swift in Sources */,
|
||||||
|
BF1FB1861C5EE643007E2494 /* SaveState.swift in Sources */,
|
||||||
BFB141181BE46934004FBF46 /* GameCollectionViewDataSource.swift in Sources */,
|
BFB141181BE46934004FBF46 /* GameCollectionViewDataSource.swift in Sources */,
|
||||||
|
BF1FB1841C5EE643007E2494 /* SaveState+CoreDataProperties.swift in Sources */,
|
||||||
BFFB709F1AF99B1700DE56FE /* EmulationViewController.swift in Sources */,
|
BFFB709F1AF99B1700DE56FE /* EmulationViewController.swift in Sources */,
|
||||||
BFAA1FF41B8AD7F900495943 /* ControllersSettingsViewController.swift in Sources */,
|
BFAA1FF41B8AD7F900495943 /* ControllersSettingsViewController.swift in Sources */,
|
||||||
BF353FF91C5D870B00C1184C /* PauseItem.swift in Sources */,
|
BF353FF91C5D870B00C1184C /* PauseItem.swift in Sources */,
|
||||||
@ -719,6 +736,7 @@
|
|||||||
BF797A2D1C2D339F00F1A000 /* UILabel+FontSize.swift in Sources */,
|
BF797A2D1C2D339F00F1A000 /* UILabel+FontSize.swift in Sources */,
|
||||||
BF3540021C5DA3D500C1184C /* PauseStoryboardSegue.swift in Sources */,
|
BF3540021C5DA3D500C1184C /* PauseStoryboardSegue.swift in Sources */,
|
||||||
BF107EC41BF413F000E0C32C /* GamesViewController.swift in Sources */,
|
BF107EC41BF413F000E0C32C /* GamesViewController.swift in Sources */,
|
||||||
|
BF172AEB1C68986300C26774 /* NSManagedObjectContext+Conveniences.swift in Sources */,
|
||||||
BF5E7F441B9A650B00AE44F8 /* SettingsViewController.swift in Sources */,
|
BF5E7F441B9A650B00AE44F8 /* SettingsViewController.swift in Sources */,
|
||||||
BF353FF21C5D7FB000C1184C /* PauseViewController.swift in Sources */,
|
BF353FF21C5D7FB000C1184C /* PauseViewController.swift in Sources */,
|
||||||
BFDB28451BC9DA7B001D0C83 /* GamePickerController.swift in Sources */,
|
BFDB28451BC9DA7B001D0C83 /* GamePickerController.swift in Sources */,
|
||||||
|
|||||||
@ -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="Dt0-nV-isV">
|
<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="Dt0-nV-isV">
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9529"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9530"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<scenes>
|
<scenes>
|
||||||
<!--Pause View Controller-->
|
<!--Pause View Controller-->
|
||||||
@ -144,11 +144,11 @@
|
|||||||
<scene sceneID="9Fi-Ti-W8T">
|
<scene sceneID="9Fi-Ti-W8T">
|
||||||
<objects>
|
<objects>
|
||||||
<collectionViewController storyboardIdentifier="saveStatesViewController" id="OOk-k7-INg" customClass="SaveStatesViewController" customModule="Delta" customModuleProvider="target" sceneMemberID="viewController">
|
<collectionViewController storyboardIdentifier="saveStatesViewController" id="OOk-k7-INg" customClass="SaveStatesViewController" customModule="Delta" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<collectionView key="view" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" id="XgF-OF-CVf">
|
<collectionView key="view" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" id="XgF-OF-CVf">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||||
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="10" minimumInteritemSpacing="10" id="tvW-q1-PD8" customClass="GridCollectionViewLayout" customModule="Delta" customModuleProvider="target">
|
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="20" minimumInteritemSpacing="20" id="tvW-q1-PD8" customClass="GridCollectionViewLayout" customModule="Delta" customModuleProvider="target">
|
||||||
<size key="itemSize" width="50" height="50"/>
|
<size key="itemSize" width="50" height="50"/>
|
||||||
<size key="headerReferenceSize" width="0.0" height="0.0"/>
|
<size key="headerReferenceSize" width="0.0" height="0.0"/>
|
||||||
<size key="footerReferenceSize" width="0.0" height="0.0"/>
|
<size key="footerReferenceSize" width="0.0" height="0.0"/>
|
||||||
@ -170,7 +170,13 @@
|
|||||||
<outlet property="delegate" destination="OOk-k7-INg" id="aLg-5i-MAd"/>
|
<outlet property="delegate" destination="OOk-k7-INg" id="aLg-5i-MAd"/>
|
||||||
</connections>
|
</connections>
|
||||||
</collectionView>
|
</collectionView>
|
||||||
<navigationItem key="navigationItem" title="Save State" id="BoG-k2-aR2"/>
|
<navigationItem key="navigationItem" title="Save State" id="BoG-k2-aR2">
|
||||||
|
<barButtonItem key="rightBarButtonItem" systemItem="add" id="lKg-Ks-hWN">
|
||||||
|
<connections>
|
||||||
|
<action selector="addSaveState" destination="OOk-k7-INg" id="xY2-94-EOr"/>
|
||||||
|
</connections>
|
||||||
|
</barButtonItem>
|
||||||
|
</navigationItem>
|
||||||
<simulatedNavigationBarMetrics key="simulatedTopBarMetrics" barStyle="black" prompted="NO"/>
|
<simulatedNavigationBarMetrics key="simulatedTopBarMetrics" barStyle="black" prompted="NO"/>
|
||||||
</collectionViewController>
|
</collectionViewController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="cL5-DH-K3b" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="cL5-DH-K3b" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
|
|||||||
@ -39,7 +39,8 @@ class EmulationViewController: UIViewController
|
|||||||
return NSStringFromClass(presentationController.dynamicType).containsString("PreviewPresentation")
|
return NSStringFromClass(presentationController.dynamicType).containsString("PreviewPresentation")
|
||||||
}
|
}
|
||||||
|
|
||||||
private var _isPauseViewControllerPresented = false
|
private var pauseViewController: PauseViewController?
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//MARK: - Initializers -
|
//MARK: - Initializers -
|
||||||
@ -120,7 +121,7 @@ class EmulationViewController: UIViewController
|
|||||||
|
|
||||||
coordinator.animateAlongsideTransition({ _ in
|
coordinator.animateAlongsideTransition({ _ in
|
||||||
|
|
||||||
if self._isPauseViewControllerPresented
|
if self.pauseViewController != nil
|
||||||
{
|
{
|
||||||
// We need to manually "refresh" the game screen, otherwise the system tries to cache the rendered image, but skews it incorrectly when rotating b/c of UIVisualEffectView
|
// We need to manually "refresh" the game screen, otherwise the system tries to cache the rendered image, but skews it incorrectly when rotating b/c of UIVisualEffectView
|
||||||
self.gameView.inputImage = self.gameView.outputImage
|
self.gameView.inputImage = self.gameView.outputImage
|
||||||
@ -148,11 +149,11 @@ class EmulationViewController: UIViewController
|
|||||||
}
|
}
|
||||||
|
|
||||||
let saveStateItem = PauseItem(image: UIImage(named: "SmallPause")!, text: NSLocalizedString("Save State", comment: ""), action: { _ in
|
let saveStateItem = PauseItem(image: UIImage(named: "SmallPause")!, text: NSLocalizedString("Save State", comment: ""), action: { _ in
|
||||||
pauseViewController.presentSaveStateViewController()
|
pauseViewController.presentSaveStateViewController(delegate: self)
|
||||||
})
|
})
|
||||||
|
|
||||||
let loadStateItem = PauseItem(image: UIImage(named: "SmallPause")!, text: NSLocalizedString("Load State", comment: ""), action: { _ in
|
let loadStateItem = PauseItem(image: UIImage(named: "SmallPause")!, text: NSLocalizedString("Load State", comment: ""), action: { _ in
|
||||||
pauseViewController.presentSaveStateViewController()
|
pauseViewController.presentSaveStateViewController(delegate: self)
|
||||||
})
|
})
|
||||||
|
|
||||||
let cheatCodesItem = PauseItem(image: UIImage(named: "SmallPause")!, text: NSLocalizedString("Cheat Codes", comment: ""), action: dismissAction)
|
let cheatCodesItem = PauseItem(image: UIImage(named: "SmallPause")!, text: NSLocalizedString("Cheat Codes", comment: ""), action: dismissAction)
|
||||||
@ -165,13 +166,13 @@ class EmulationViewController: UIViewController
|
|||||||
|
|
||||||
pauseViewController.items = [saveStateItem, loadStateItem, cheatCodesItem, fastForwardItem, sustainButtonItem]
|
pauseViewController.items = [saveStateItem, loadStateItem, cheatCodesItem, fastForwardItem, sustainButtonItem]
|
||||||
|
|
||||||
self._isPauseViewControllerPresented = true
|
self.pauseViewController = pauseViewController
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func unwindFromPauseViewController(segue: UIStoryboardSegue)
|
@IBAction func unwindFromPauseViewController(segue: UIStoryboardSegue)
|
||||||
{
|
{
|
||||||
self._isPauseViewControllerPresented = false
|
self.pauseViewController = nil
|
||||||
|
|
||||||
self.emulatorCore.resumeEmulation()
|
self.emulatorCore.resumeEmulation()
|
||||||
}
|
}
|
||||||
@ -217,6 +218,46 @@ private extension EmulationViewController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//MARK: - Save States
|
||||||
|
/// Save States
|
||||||
|
extension EmulationViewController: SaveStatesViewControllerDelegate
|
||||||
|
{
|
||||||
|
func saveStatesViewControllerActiveGame(saveStatesViewController: SaveStatesViewController) -> Game
|
||||||
|
{
|
||||||
|
return self.game
|
||||||
|
}
|
||||||
|
|
||||||
|
func saveStatesViewController(saveStatesViewController: SaveStatesViewController, updateSaveState saveState: SaveState)
|
||||||
|
{
|
||||||
|
guard let filepath = saveState.fileURL.path else { return }
|
||||||
|
|
||||||
|
self.emulatorCore.saveSaveState { temporarySaveState in
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if NSFileManager.defaultManager().fileExistsAtPath(filepath)
|
||||||
|
{
|
||||||
|
try NSFileManager.defaultManager().replaceItemAtURL(saveState.fileURL, withItemAtURL: temporarySaveState.fileURL, backupItemName: nil, options: [], resultingItemURL: nil)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
try NSFileManager.defaultManager().moveItemAtURL(temporarySaveState.fileURL, toURL: saveState.fileURL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch let error as NSError
|
||||||
|
{
|
||||||
|
print(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func saveStatesViewController(saveStatesViewController: SaveStatesViewController, loadSaveState saveState: SaveState)
|
||||||
|
{
|
||||||
|
self.emulatorCore.loadSaveState(saveState)
|
||||||
|
|
||||||
|
self.pauseViewController?.dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//MARK: - <GameControllerReceiver> -
|
//MARK: - <GameControllerReceiver> -
|
||||||
/// <GameControllerReceiver>
|
/// <GameControllerReceiver>
|
||||||
extension EmulationViewController: GameControllerReceiverType
|
extension EmulationViewController: GameControllerReceiverType
|
||||||
|
|||||||
@ -16,6 +16,8 @@ class PauseViewController: UIViewController, PauseInfoProvidable
|
|||||||
/// <PauseInfoProvidable>
|
/// <PauseInfoProvidable>
|
||||||
var pauseText: String? = nil
|
var pauseText: String? = nil
|
||||||
|
|
||||||
|
private weak var saveStatesViewControllerDelegate: SaveStatesViewControllerDelegate?
|
||||||
|
|
||||||
/// UIViewController
|
/// UIViewController
|
||||||
override var preferredContentSize: CGSize {
|
override var preferredContentSize: CGSize {
|
||||||
set { }
|
set { }
|
||||||
@ -69,8 +71,9 @@ extension PauseViewController
|
|||||||
|
|
||||||
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
|
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
|
||||||
{
|
{
|
||||||
if segue.identifier == "embedNavigationController"
|
switch segue.identifier ?? ""
|
||||||
{
|
{
|
||||||
|
case "embedNavigationController":
|
||||||
self.pauseNavigationController = segue.destinationViewController as! UINavigationController
|
self.pauseNavigationController = segue.destinationViewController as! UINavigationController
|
||||||
self.pauseNavigationController.delegate = self
|
self.pauseNavigationController.delegate = self
|
||||||
self.pauseNavigationController.navigationBar.tintColor = UIColor.deltaLightPurpleColor()
|
self.pauseNavigationController.navigationBar.tintColor = UIColor.deltaLightPurpleColor()
|
||||||
@ -81,6 +84,12 @@ extension PauseViewController
|
|||||||
|
|
||||||
// Keep navigation bar outside the UIVisualEffectView's
|
// Keep navigation bar outside the UIVisualEffectView's
|
||||||
self.view.addSubview(self.pauseNavigationController.navigationBar)
|
self.view.addSubview(self.pauseNavigationController.navigationBar)
|
||||||
|
|
||||||
|
case "saveState":
|
||||||
|
let saveStatesViewController = segue.destinationViewController as! SaveStatesViewController
|
||||||
|
saveStatesViewController.delegate = self.saveStatesViewControllerDelegate
|
||||||
|
|
||||||
|
default: break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -92,8 +101,10 @@ extension PauseViewController
|
|||||||
self.performSegueWithIdentifier("unwindFromPauseMenu", sender: self)
|
self.performSegueWithIdentifier("unwindFromPauseMenu", sender: self)
|
||||||
}
|
}
|
||||||
|
|
||||||
func presentSaveStateViewController()
|
func presentSaveStateViewController(delegate delegate: SaveStatesViewControllerDelegate)
|
||||||
{
|
{
|
||||||
|
self.saveStatesViewControllerDelegate = delegate
|
||||||
|
|
||||||
self.performSegueWithIdentifier("saveState", sender: self)
|
self.performSegueWithIdentifier("saveState", sender: self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,17 +7,47 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import CoreData
|
||||||
|
|
||||||
|
import DeltaCore
|
||||||
import Roxas
|
import Roxas
|
||||||
|
|
||||||
private let SaveStatesViewControllerContentInset: CGFloat = 20
|
protocol SaveStatesViewControllerDelegate: class
|
||||||
|
{
|
||||||
|
func saveStatesViewControllerActiveGame(saveStatesViewController: SaveStatesViewController) -> Game
|
||||||
|
func saveStatesViewController(saveStatesViewController: SaveStatesViewController, updateSaveState saveState: SaveState)
|
||||||
|
func saveStatesViewController(saveStatesViewController: SaveStatesViewController, loadSaveState saveState: SaveState)
|
||||||
|
}
|
||||||
|
|
||||||
class SaveStatesViewController: UICollectionViewController
|
class SaveStatesViewController: UICollectionViewController
|
||||||
{
|
{
|
||||||
|
weak var delegate: SaveStatesViewControllerDelegate?
|
||||||
|
|
||||||
private var backgroundView: RSTBackgroundView!
|
private var backgroundView: RSTBackgroundView!
|
||||||
|
|
||||||
private var prototypeCell = GridCollectionViewCell()
|
private var prototypeCell = GridCollectionViewCell()
|
||||||
private var prototypeCellWidthConstraint: NSLayoutConstraint!
|
private var prototypeCellWidthConstraint: NSLayoutConstraint!
|
||||||
|
|
||||||
|
private let fetchedResultsController: NSFetchedResultsController
|
||||||
|
|
||||||
|
private let dateFormatter: NSDateFormatter
|
||||||
|
|
||||||
|
required init?(coder aDecoder: NSCoder)
|
||||||
|
{
|
||||||
|
let fetchRequest = SaveState.fetchRequest()
|
||||||
|
fetchRequest.returnsObjectsAsFaults = false
|
||||||
|
fetchRequest.sortDescriptors = [NSSortDescriptor(key: SaveStateAttributes.creationDate.rawValue, ascending: true)]
|
||||||
|
|
||||||
|
self.fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: DatabaseManager.sharedManager.managedObjectContext, sectionNameKeyPath: nil, cacheName: nil)
|
||||||
|
|
||||||
|
self.dateFormatter = NSDateFormatter()
|
||||||
|
self.dateFormatter.timeStyle = .ShortStyle
|
||||||
|
self.dateFormatter.dateStyle = .ShortStyle
|
||||||
|
|
||||||
|
super.init(coder: aDecoder)
|
||||||
|
|
||||||
|
self.fetchedResultsController.delegate = self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SaveStatesViewController
|
extension SaveStatesViewController
|
||||||
@ -35,20 +65,38 @@ extension SaveStatesViewController
|
|||||||
self.backgroundView.detailTextLabel.textColor = UIColor.whiteColor()
|
self.backgroundView.detailTextLabel.textColor = UIColor.whiteColor()
|
||||||
self.view.insertSubview(self.backgroundView, atIndex: 0)
|
self.view.insertSubview(self.backgroundView, atIndex: 0)
|
||||||
|
|
||||||
// We update the layout in code because we need to use our SaveStatesViewControllerContentInset constant
|
|
||||||
// The reason for this is we cannot query the layout for its sectionInset in viewDidLayoutSubviews, so might as well be explicit in code with a constant
|
|
||||||
// Otherwise, we could configure this all in Interface Builder, but we'd still need to hardcode 20 in for viewDidLayoutSubviews
|
|
||||||
let collectionViewLayout = self.collectionViewLayout as! GridCollectionViewLayout
|
let collectionViewLayout = self.collectionViewLayout as! GridCollectionViewLayout
|
||||||
collectionViewLayout.sectionInset = UIEdgeInsets(top: SaveStatesViewControllerContentInset, left: SaveStatesViewControllerContentInset, bottom: SaveStatesViewControllerContentInset, right: SaveStatesViewControllerContentInset)
|
let averageHorizontalInset = (collectionViewLayout.sectionInset.left + collectionViewLayout.sectionInset.right) / 2
|
||||||
collectionViewLayout.minimumInteritemSpacing = SaveStatesViewControllerContentInset
|
|
||||||
collectionViewLayout.minimumLineSpacing = SaveStatesViewControllerContentInset
|
|
||||||
|
|
||||||
let portraitScreenWidth = UIScreen.mainScreen().coordinateSpace.convertRect(UIScreen.mainScreen().bounds, toCoordinateSpace: UIScreen.mainScreen().fixedCoordinateSpace).width
|
let portraitScreenWidth = UIScreen.mainScreen().coordinateSpace.convertRect(UIScreen.mainScreen().bounds, toCoordinateSpace: UIScreen.mainScreen().fixedCoordinateSpace).width
|
||||||
collectionViewLayout.itemWidth = (portraitScreenWidth - ((SaveStatesViewControllerContentInset) * 3)) / 2
|
|
||||||
|
// Use dimensions that allow two cells to fill the screen horizontally with padding in portrait mode
|
||||||
|
// We'll keep the same size for landscape orientation, which will allow more to fit
|
||||||
|
collectionViewLayout.itemWidth = (portraitScreenWidth - (averageHorizontalInset * 3)) / 2
|
||||||
|
|
||||||
// Manually update prototype cell properties
|
// Manually update prototype cell properties
|
||||||
self.prototypeCellWidthConstraint = self.prototypeCell.contentView.widthAnchor.constraintEqualToConstant(collectionViewLayout.itemWidth)
|
self.prototypeCellWidthConstraint = self.prototypeCell.contentView.widthAnchor.constraintEqualToConstant(collectionViewLayout.itemWidth)
|
||||||
self.prototypeCellWidthConstraint.active = true
|
self.prototypeCellWidthConstraint.active = true
|
||||||
|
|
||||||
|
self.updateBackgroundView()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewWillAppear(animated: Bool)
|
||||||
|
{
|
||||||
|
if self.fetchedResultsController.fetchedObjects == nil
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
try self.fetchedResultsController.performFetch()
|
||||||
|
}
|
||||||
|
catch let error as NSError
|
||||||
|
{
|
||||||
|
print(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.updateBackgroundView()
|
||||||
|
|
||||||
|
super.viewWillAppear(animated)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func didReceiveMemoryWarning()
|
override func didReceiveMemoryWarning()
|
||||||
@ -57,10 +105,27 @@ extension SaveStatesViewController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private extension SaveStatesViewController
|
||||||
|
{
|
||||||
|
func updateBackgroundView()
|
||||||
|
{
|
||||||
|
if let fetchedObjects = self.fetchedResultsController.fetchedObjects where fetchedObjects.count > 0
|
||||||
|
{
|
||||||
|
self.backgroundView.hidden = true
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
self.backgroundView.hidden = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private extension SaveStatesViewController
|
private extension SaveStatesViewController
|
||||||
{
|
{
|
||||||
func configureCollectionViewCell(cell: GridCollectionViewCell, forIndexPath indexPath: NSIndexPath)
|
func configureCollectionViewCell(cell: GridCollectionViewCell, forIndexPath indexPath: NSIndexPath)
|
||||||
{
|
{
|
||||||
|
let saveState = self.fetchedResultsController.objectAtIndexPath(indexPath) as! SaveState
|
||||||
|
|
||||||
cell.imageView.backgroundColor = UIColor.whiteColor()
|
cell.imageView.backgroundColor = UIColor.whiteColor()
|
||||||
cell.imageView.image = UIImage(named: "DeltaPlaceholder")
|
cell.imageView.image = UIImage(named: "DeltaPlaceholder")
|
||||||
|
|
||||||
@ -68,7 +133,42 @@ private extension SaveStatesViewController
|
|||||||
|
|
||||||
cell.textLabel.textColor = UIColor.whiteColor()
|
cell.textLabel.textColor = UIColor.whiteColor()
|
||||||
cell.textLabel.font = UIFont.preferredFontForTextStyle(UIFontTextStyleSubheadline)
|
cell.textLabel.font = UIFont.preferredFontForTextStyle(UIFontTextStyleSubheadline)
|
||||||
cell.textLabel.text = "Save State"
|
|
||||||
|
let name = saveState.name ?? self.dateFormatter.stringFromDate(saveState.modifiedDate)
|
||||||
|
cell.textLabel.text = name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension SaveStatesViewController
|
||||||
|
{
|
||||||
|
@IBAction func addSaveState()
|
||||||
|
{
|
||||||
|
guard let delegate = self.delegate else { return }
|
||||||
|
|
||||||
|
let backgroundContext = DatabaseManager.sharedManager.backgroundManagedObjectContext()
|
||||||
|
backgroundContext.performBlock {
|
||||||
|
|
||||||
|
let identifier = NSUUID().UUIDString
|
||||||
|
let date = NSDate()
|
||||||
|
|
||||||
|
var game = delegate.saveStatesViewControllerActiveGame(self)
|
||||||
|
game = backgroundContext.objectWithID(game.objectID) as! Game
|
||||||
|
|
||||||
|
let saveState = SaveState.insertIntoManagedObjectContext(backgroundContext)
|
||||||
|
saveState.identifier = identifier
|
||||||
|
saveState.filename = identifier
|
||||||
|
saveState.creationDate = date
|
||||||
|
saveState.modifiedDate = date
|
||||||
|
saveState.game = game
|
||||||
|
|
||||||
|
self.updateSaveState(saveState)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateSaveState(saveState: SaveState)
|
||||||
|
{
|
||||||
|
self.delegate?.saveStatesViewController(self, updateSaveState: saveState)
|
||||||
|
saveState.managedObjectContext?.saveWithErrorLogging()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,7 +176,8 @@ extension SaveStatesViewController
|
|||||||
{
|
{
|
||||||
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
|
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
|
||||||
{
|
{
|
||||||
return 12
|
let section = self.fetchedResultsController.sections![section]
|
||||||
|
return section.numberOfObjects
|
||||||
}
|
}
|
||||||
|
|
||||||
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell
|
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell
|
||||||
@ -87,6 +188,15 @@ extension SaveStatesViewController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension SaveStatesViewController
|
||||||
|
{
|
||||||
|
override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath)
|
||||||
|
{
|
||||||
|
let saveState = self.fetchedResultsController.objectAtIndexPath(indexPath) as! SaveState
|
||||||
|
self.delegate?.saveStatesViewController(self, loadSaveState: saveState)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extension SaveStatesViewController: UICollectionViewDelegateFlowLayout
|
extension SaveStatesViewController: UICollectionViewDelegateFlowLayout
|
||||||
{
|
{
|
||||||
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize
|
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize
|
||||||
@ -96,4 +206,13 @@ extension SaveStatesViewController: UICollectionViewDelegateFlowLayout
|
|||||||
let size = self.prototypeCell.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
|
let size = self.prototypeCell.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
|
||||||
return size
|
return size
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension SaveStatesViewController: NSFetchedResultsControllerDelegate
|
||||||
|
{
|
||||||
|
func controllerDidChangeContent(controller: NSFetchedResultsController)
|
||||||
|
{
|
||||||
|
self.collectionView?.reloadData()
|
||||||
|
self.updateBackgroundView()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
2
External/Roxas
vendored
2
External/Roxas
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 41ed3e9fcc247258a83e103f8816dbc07652c75a
|
Subproject commit 6dc906703bb5870d268d341cefd1fdcc40001eb9
|
||||||
Loading…
Reference in New Issue
Block a user