Adds ability to select and use custom controller skins

This commit is contained in:
Riley Testut 2016-11-16 20:48:05 -08:00
parent 2b0fe3ef20
commit 8cd3577cdc
5 changed files with 161 additions and 7 deletions

@ -1 +1 @@
Subproject commit b2af08a2db9e371b4fcfd8aa52b5106407af7c09
Subproject commit 35d634fdeec4f89da7dfecacb90188d688caa177

View File

@ -46,6 +46,8 @@ class GameViewController: DeltaCore.GameViewController
let game = self.game as? Game
NotificationCenter.default.addObserver(self, selector: #selector(GameViewController.managedObjectContextDidChange(with:)), name: .NSManagedObjectContextObjectsDidChange, object: game?.managedObjectContext)
self.updateControllerSkin()
}
}
@ -110,6 +112,7 @@ class GameViewController: DeltaCore.GameViewController
NotificationCenter.default.addObserver(self, selector: #selector(GameViewController.updateControllers), name: .externalControllerDidConnect, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(GameViewController.updateControllers), name: .externalControllerDidDisconnect, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(GameViewController.didEnterBackground(with:)), name: .UIApplicationDidEnterBackground, object: UIApplication.shared)
NotificationCenter.default.addObserver(self, selector: #selector(GameViewController.preferredControllerSkinDidUpdate(with:)), name: .preferredControllerSkinDidUpdate, object: nil)
}
deinit
@ -186,9 +189,26 @@ extension GameViewController
self.sustainButtonsContentView.topAnchor.constraint(equalTo: self.gameView.topAnchor).isActive = true
self.sustainButtonsContentView.bottomAnchor.constraint(equalTo: self.gameView.bottomAnchor).isActive = true
self.updateControllerSkin()
self.updateControllers()
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator)
{
super.viewWillTransition(to: size, with: coordinator)
guard let game = self.game else { return }
var traits = DeltaCore.ControllerSkin.Traits.defaults(for: self.view)
traits.orientation = (size.width > size.height) ? .landscape : .portrait
let controllerSkin = Settings.preferredControllerSkin(for: game.type, traits: traits)
coordinator.animate(alongsideTransition: { (context) in
self.controllerView.controllerSkin = controllerSkin
}, completion: nil)
}
// MARK: - Segues
/// KVO
@ -360,6 +380,16 @@ private extension GameViewController
self.view.setNeedsLayout()
}
func updateControllerSkin()
{
guard let game = self.game else { return }
let traits = DeltaCore.ControllerSkin.Traits.defaults(for: self.view)
let controllerSkin = Settings.preferredControllerSkin(for: game.type, traits: traits)
self.controllerView.controllerSkin = controllerSkin
}
}
//MARK: - Save States -
@ -731,4 +761,18 @@ private extension GameViewController
self.game = nil
}
}
@objc func preferredControllerSkinDidUpdate(with notification: Notification)
{
guard
let gameType = notification.userInfo?[Settings.NotificationUserInfoKey.gameType.rawValue] as? GameType,
let traits = notification.userInfo?[Settings.NotificationUserInfoKey.traits.rawValue] as? DeltaCore.ControllerSkin.Traits
else { return }
let currentTraits = DeltaCore.ControllerSkin.Traits.defaults(for: self.view)
if gameType == self.game?.type && traits == currentTraits
{
self.updateControllerSkin()
}
}
}

View File

@ -178,6 +178,14 @@ extension ControllerSkinsViewController: UITableViewDataSourcePrefetching
extension ControllerSkinsViewController
{
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
let controllerSkin = self.dataSource.fetchedResultsController.object(at: indexPath)
Settings.setPreferredControllerSkin(controllerSkin, for: self.gameType, traits: self.traits)
_ = self.navigationController?.popViewController(animated: true)
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
{
let controllerSkin = self.dataSource.fetchedResultsController.object(at: indexPath)

View File

@ -102,12 +102,13 @@ private extension GameTypeControllerSkinsViewController
{
func updateControllerSkins()
{
let controllerSkin = DeltaCore.ControllerSkin.standardControllerSkin(for: self.gameType)
let portraitTraits = DeltaCore.ControllerSkin.Traits(deviceType: .iphone, displayMode: DeltaCore.ControllerSkin.DisplayMode.fullScreen, orientation: .portrait)
self.portraitImageView.image = controllerSkin?.image(for: portraitTraits, preferredSize: UIScreen.main.defaultControllerSkinSize)
let landscapeTraits = DeltaCore.ControllerSkin.Traits(deviceType: .iphone, displayMode: DeltaCore.ControllerSkin.DisplayMode.fullScreen, orientation: .landscape)
self.landscapeImageView.image = controllerSkin?.image(for: landscapeTraits, preferredSize: UIScreen.main.defaultControllerSkinSize)
let portraitControllerSkin = Settings.preferredControllerSkin(for: self.gameType, traits: portraitTraits)
let landscapeControllerSkin = Settings.preferredControllerSkin(for: self.gameType, traits: landscapeTraits)
self.portraitImageView.image = portraitControllerSkin?.image(for: portraitTraits, preferredSize: UIScreen.main.defaultControllerSkinSize)
self.landscapeImageView.image = landscapeControllerSkin?.image(for: landscapeTraits, preferredSize: UIScreen.main.defaultControllerSkinSize)
}
}

View File

@ -8,9 +8,110 @@
import Foundation
import DeltaCore
import SNESDeltaCore
import GBADeltaCore
import Roxas
extension Notification.Name
{
static let preferredControllerSkinDidUpdate = Notification.Name("PreferredControllerSkinDidUpdateNotification")
}
extension Settings
{
enum NotificationUserInfoKey: String
{
case gameType
case traits
}
}
struct Settings
{
/// Controllers
static var localControllerPlayerIndex: Int? = 0
}
static func preferredControllerSkin(for gameType: GameType, traits: DeltaCore.ControllerSkin.Traits) -> ControllerSkin?
{
guard let userDefaultsKey = self.preferredControllerSkinKey(for: gameType, traits: traits) else { return nil }
let identifier = UserDefaults.standard.string(forKey: userDefaultsKey)
do
{
// Attempt to load preferred controller skin if it exists
let fetchRequest: NSFetchRequest<ControllerSkin> = ControllerSkin.fetchRequest()
if let identifier = identifier
{
fetchRequest.predicate = NSPredicate(format: "%K == %@ AND %K == %@", #keyPath(ControllerSkin.gameType), gameType.rawValue, #keyPath(ControllerSkin.identifier), identifier)
if let controllerSkin = try DatabaseManager.shared.viewContext.fetch(fetchRequest).first
{
return controllerSkin
}
}
// Controller skin doesn't exist, so fall back to standard controller skin
fetchRequest.predicate = NSPredicate(format: "%K == %@ AND %K == YES", #keyPath(ControllerSkin.gameType), gameType.rawValue, #keyPath(ControllerSkin.isStandard))
if let controllerSkin = try DatabaseManager.shared.viewContext.fetch(fetchRequest).first
{
Settings.setPreferredControllerSkin(controllerSkin, for: gameType, traits: traits)
return controllerSkin
}
}
catch
{
print(error)
}
return nil
}
static func setPreferredControllerSkin(_ controllerSkin: ControllerSkin, for gameType: GameType, traits: DeltaCore.ControllerSkin.Traits)
{
guard let userDefaultKey = self.preferredControllerSkinKey(for: gameType, traits: traits) else { return }
UserDefaults.standard.set(controllerSkin.identifier, forKey: userDefaultKey)
NotificationCenter.default.post(name: .preferredControllerSkinDidUpdate, object: controllerSkin, userInfo: [NotificationUserInfoKey.gameType.rawValue: gameType, NotificationUserInfoKey.traits.rawValue: traits])
}
}
private extension Settings
{
static func preferredControllerSkinKey(for gameType: GameType, traits: DeltaCore.ControllerSkin.Traits) -> String?
{
let systemName: String
switch gameType
{
case GameType.snes: systemName = "snes"
case GameType.gba: systemName = "gba"
default: return nil
}
let orientation: String
switch traits.orientation
{
case .portrait: orientation = "portrait"
case .landscape: orientation = "landscape"
}
let displayMode: String
switch traits.displayMode
{
case .fullScreen: displayMode = "fullscreen"
case .splitView: displayMode = "splitview"
}
let key = systemName + "-" + orientation + "-" + displayMode + "-controller"
return key
}
}