Merge branch 'ipad' into develop
# Conflicts: # Delta.xcodeproj/project.pbxproj
This commit is contained in:
commit
7c934cebe1
@ -1 +1 @@
|
|||||||
Subproject commit e2b3f0e46b4c64670e13fd0466ebdac719f84555
|
Subproject commit 2a6779e1271bc5d2e09aea2aa41fa6a0b75b62aa
|
||||||
@ -168,6 +168,7 @@
|
|||||||
D524F4A1273DE9A100D500B2 /* AltKit in Frameworks */ = {isa = PBXBuildFile; productRef = D524F4A0273DE9A100D500B2 /* AltKit */; };
|
D524F4A1273DE9A100D500B2 /* AltKit in Frameworks */ = {isa = PBXBuildFile; productRef = D524F4A0273DE9A100D500B2 /* AltKit */; };
|
||||||
D524F4A3273DE9C000D500B2 /* ProcessInfo+JIT.swift in Sources */ = {isa = PBXBuildFile; fileRef = D524F4A2273DE9C000D500B2 /* ProcessInfo+JIT.swift */; };
|
D524F4A3273DE9C000D500B2 /* ProcessInfo+JIT.swift in Sources */ = {isa = PBXBuildFile; fileRef = D524F4A2273DE9C000D500B2 /* ProcessInfo+JIT.swift */; };
|
||||||
D524F4A5273DEBB400D500B2 /* ServerManager+Delta.swift in Sources */ = {isa = PBXBuildFile; fileRef = D524F4A4273DEBB400D500B2 /* ServerManager+Delta.swift */; };
|
D524F4A5273DEBB400D500B2 /* ServerManager+Delta.swift in Sources */ = {isa = PBXBuildFile; fileRef = D524F4A4273DEBB400D500B2 /* ServerManager+Delta.swift */; };
|
||||||
|
D5011C48281B6E8B00A0760B /* CharacterSet+Filename.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5011C47281B6E8B00A0760B /* CharacterSet+Filename.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
@ -363,6 +364,7 @@
|
|||||||
C786AF1D2DDB6223BE2063CC /* Pods-Delta.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Delta.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Delta/Pods-Delta.debug.xcconfig"; sourceTree = "<group>"; };
|
C786AF1D2DDB6223BE2063CC /* Pods-Delta.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Delta.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Delta/Pods-Delta.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
D524F4A2273DE9C000D500B2 /* ProcessInfo+JIT.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProcessInfo+JIT.swift"; sourceTree = "<group>"; };
|
D524F4A2273DE9C000D500B2 /* ProcessInfo+JIT.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProcessInfo+JIT.swift"; sourceTree = "<group>"; };
|
||||||
D524F4A4273DEBB400D500B2 /* ServerManager+Delta.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ServerManager+Delta.swift"; sourceTree = "<group>"; };
|
D524F4A4273DEBB400D500B2 /* ServerManager+Delta.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ServerManager+Delta.swift"; sourceTree = "<group>"; };
|
||||||
|
D5011C47281B6E8B00A0760B /* CharacterSet+Filename.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CharacterSet+Filename.swift"; sourceTree = "<group>"; };
|
||||||
DC866E433B3BA9AE18ABA1EC /* libPods-Delta.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Delta.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
DC866E433B3BA9AE18ABA1EC /* libPods-Delta.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Delta.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
@ -411,6 +413,7 @@
|
|||||||
BFE56E1823EB7BE00014FECD /* UIImage+SymbolFallback.swift */,
|
BFE56E1823EB7BE00014FECD /* UIImage+SymbolFallback.swift */,
|
||||||
D524F4A2273DE9C000D500B2 /* ProcessInfo+JIT.swift */,
|
D524F4A2273DE9C000D500B2 /* ProcessInfo+JIT.swift */,
|
||||||
D524F4A4273DEBB400D500B2 /* ServerManager+Delta.swift */,
|
D524F4A4273DEBB400D500B2 /* ServerManager+Delta.swift */,
|
||||||
|
D5011C47281B6E8B00A0760B /* CharacterSet+Filename.swift */,
|
||||||
);
|
);
|
||||||
path = Extensions;
|
path = Extensions;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -1126,6 +1129,7 @@
|
|||||||
BF525EEA1FF6CD12004AA849 /* DeepLink.swift in Sources */,
|
BF525EEA1FF6CD12004AA849 /* DeepLink.swift in Sources */,
|
||||||
BF8A334621A4926F00A42FD4 /* GameSyncStatusViewController.swift in Sources */,
|
BF8A334621A4926F00A42FD4 /* GameSyncStatusViewController.swift in Sources */,
|
||||||
BF59427E1E09BC830051894B /* Game.swift in Sources */,
|
BF59427E1E09BC830051894B /* Game.swift in Sources */,
|
||||||
|
D5011C48281B6E8B00A0760B /* CharacterSet+Filename.swift in Sources */,
|
||||||
BFE593CC21F3F8C2003412A6 /* _GameSave.swift in Sources */,
|
BFE593CC21F3F8C2003412A6 /* _GameSave.swift in Sources */,
|
||||||
BF63A1A321A4AAAE00EE8F61 /* RecordSyncStatusViewController.swift in Sources */,
|
BF63A1A321A4AAAE00EE8F61 /* RecordSyncStatusViewController.swift in Sources */,
|
||||||
BFAA1FED1B8AA4FA00495943 /* Settings.swift in Sources */,
|
BFAA1FED1B8AA4FA00495943 /* Settings.swift in Sources */,
|
||||||
@ -1453,6 +1457,7 @@
|
|||||||
SWIFT_OBJC_BRIDGING_HEADER = "Delta/Supporting Files/Delta-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Delta/Supporting Files/Delta-Bridging-Header.h";
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
};
|
};
|
||||||
@ -1482,6 +1487,7 @@
|
|||||||
SWIFT_OBJC_BRIDGING_HEADER = "Delta/Supporting Files/Delta-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Delta/Supporting Files/Delta-Bridging-Header.h";
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -46,6 +46,9 @@
|
|||||||
</connections>
|
</connections>
|
||||||
</barButtonItem>
|
</barButtonItem>
|
||||||
</navigationItem>
|
</navigationItem>
|
||||||
|
<connections>
|
||||||
|
<outlet property="importButton" destination="FeA-O5-xd2" id="A44-3S-Okz"/>
|
||||||
|
</connections>
|
||||||
</viewController>
|
</viewController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="JYx-xE-nis" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="JYx-xE-nis" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
|
|||||||
@ -1048,6 +1048,7 @@
|
|||||||
<objects>
|
<objects>
|
||||||
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="0QR-U9-gtx" customClass="RSTNavigationController" sceneMemberID="viewController">
|
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="0QR-U9-gtx" customClass="RSTNavigationController" sceneMemberID="viewController">
|
||||||
<toolbarItems/>
|
<toolbarItems/>
|
||||||
|
<value key="contentSizeForViewInPopover" type="size" width="375" height="667"/>
|
||||||
<simulatedNavigationBarMetrics key="simulatedTopBarMetrics" barStyle="black" prompted="NO"/>
|
<simulatedNavigationBarMetrics key="simulatedTopBarMetrics" barStyle="black" prompted="NO"/>
|
||||||
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" barStyle="black" id="Y5H-O6-CQ5">
|
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" barStyle="black" id="Y5H-O6-CQ5">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
|
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
|
||||||
|
|||||||
@ -15,6 +15,8 @@ extension UIActivity.ActivityType
|
|||||||
|
|
||||||
class CopyDeepLinkActivity: UIActivity
|
class CopyDeepLinkActivity: UIActivity
|
||||||
{
|
{
|
||||||
|
private var deepLink: URL?
|
||||||
|
|
||||||
override class var activityCategory: UIActivity.Category {
|
override class var activityCategory: UIActivity.Category {
|
||||||
return .action
|
return .action
|
||||||
}
|
}
|
||||||
@ -28,7 +30,7 @@ class CopyDeepLinkActivity: UIActivity
|
|||||||
}
|
}
|
||||||
|
|
||||||
override var activityImage: UIImage? {
|
override var activityImage: UIImage? {
|
||||||
return UIImage(named: "Link")
|
return UIImage(symbolNameIfAvailable: "link") ?? UIImage(named: "Link")
|
||||||
}
|
}
|
||||||
|
|
||||||
override func canPerform(withActivityItems activityItems: [Any]) -> Bool
|
override func canPerform(withActivityItems activityItems: [Any]) -> Bool
|
||||||
@ -47,7 +49,19 @@ class CopyDeepLinkActivity: UIActivity
|
|||||||
{
|
{
|
||||||
guard let game = activityItems.first(where: { $0 is Game }) as? Game else { return }
|
guard let game = activityItems.first(where: { $0 is Game }) as? Game else { return }
|
||||||
|
|
||||||
let deepLink = URL(action: .launchGame(identifier: game.identifier))
|
self.deepLink = URL(action: .launchGame(identifier: game.identifier))
|
||||||
|
}
|
||||||
|
|
||||||
|
override func perform()
|
||||||
|
{
|
||||||
|
if let deepLink = self.deepLink
|
||||||
|
{
|
||||||
UIPasteboard.general.url = deepLink
|
UIPasteboard.general.url = deepLink
|
||||||
|
self.activityDidFinish(true)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
self.activityDidFinish(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
22
Delta/Extensions/CharacterSet+Filename.swift
Normal file
22
Delta/Extensions/CharacterSet+Filename.swift
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
//
|
||||||
|
// CharacterSet+Filename.swift
|
||||||
|
// Delta
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 4/28/22.
|
||||||
|
// Copyright © 2022 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension CharacterSet
|
||||||
|
{
|
||||||
|
// Different than .urlPathAllowed
|
||||||
|
// Copied from https://stackoverflow.com/a/39443252
|
||||||
|
static var urlFilenameAllowed: CharacterSet {
|
||||||
|
var illegalCharacters = CharacterSet(charactersIn: ":/")
|
||||||
|
illegalCharacters.formUnion(.newlines)
|
||||||
|
illegalCharacters.formUnion(.illegalCharacters)
|
||||||
|
illegalCharacters.formUnion(.controlCharacters)
|
||||||
|
return illegalCharacters.inverted
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -66,6 +66,8 @@ class GameCollectionViewController: UICollectionViewController
|
|||||||
private weak var _previewTransitionViewController: PreviewGameViewController?
|
private weak var _previewTransitionViewController: PreviewGameViewController?
|
||||||
private weak var _previewTransitionDestinationViewController: UIViewController?
|
private weak var _previewTransitionDestinationViewController: UIViewController?
|
||||||
|
|
||||||
|
private weak var _popoverSourceView: UIView?
|
||||||
|
|
||||||
private var _renameAction: UIAlertAction?
|
private var _renameAction: UIAlertAction?
|
||||||
private var _changingArtworkGame: Game?
|
private var _changingArtworkGame: Game?
|
||||||
private var _importingSaveFileGame: Game?
|
private var _importingSaveFileGame: Game?
|
||||||
@ -93,10 +95,6 @@ extension GameCollectionViewController
|
|||||||
self.collectionView?.prefetchDataSource = self.dataSource
|
self.collectionView?.prefetchDataSource = self.dataSource
|
||||||
self.collectionView?.delegate = self
|
self.collectionView?.delegate = self
|
||||||
|
|
||||||
let layout = self.collectionViewLayout as! GridCollectionViewLayout
|
|
||||||
layout.itemWidth = 90
|
|
||||||
layout.minimumInteritemSpacing = 12
|
|
||||||
|
|
||||||
if #available(iOS 13, *) {}
|
if #available(iOS 13, *) {}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -105,6 +103,8 @@ extension GameCollectionViewController
|
|||||||
let longPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(GameCollectionViewController.handleLongPressGesture(_:)))
|
let longPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(GameCollectionViewController.handleLongPressGesture(_:)))
|
||||||
self.collectionView?.addGestureRecognizer(longPressGestureRecognizer)
|
self.collectionView?.addGestureRecognizer(longPressGestureRecognizer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.update()
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewWillDisappear(_ animated: Bool)
|
override func viewWillDisappear(_ animated: Bool)
|
||||||
@ -131,6 +131,13 @@ extension GameCollectionViewController
|
|||||||
super.didReceiveMemoryWarning()
|
super.didReceiveMemoryWarning()
|
||||||
// Dispose of any resources that can be recreated.
|
// Dispose of any resources that can be recreated.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?)
|
||||||
|
{
|
||||||
|
super.traitCollectionDidChange(previousTraitCollection)
|
||||||
|
|
||||||
|
self.update()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//MARK: - Segues -
|
//MARK: - Segues -
|
||||||
@ -224,6 +231,26 @@ extension GameCollectionViewController
|
|||||||
//MARK: - Private Methods -
|
//MARK: - Private Methods -
|
||||||
private extension GameCollectionViewController
|
private extension GameCollectionViewController
|
||||||
{
|
{
|
||||||
|
func update()
|
||||||
|
{
|
||||||
|
let layout = self.collectionViewLayout as! GridCollectionViewLayout
|
||||||
|
|
||||||
|
switch self.traitCollection.horizontalSizeClass
|
||||||
|
{
|
||||||
|
case .regular:
|
||||||
|
layout.itemWidth = 150
|
||||||
|
layout.minimumInteritemSpacing = 25 // 30 == only 3 games per line for iPad mini 6 in portrait
|
||||||
|
|
||||||
|
case .unspecified, .compact:
|
||||||
|
layout.itemWidth = 90
|
||||||
|
layout.minimumInteritemSpacing = 12
|
||||||
|
|
||||||
|
@unknown default: break
|
||||||
|
}
|
||||||
|
|
||||||
|
self.collectionView.reloadData()
|
||||||
|
}
|
||||||
|
|
||||||
//MARK: - Data Source
|
//MARK: - Data Source
|
||||||
func prepareDataSource()
|
func prepareDataSource()
|
||||||
{
|
{
|
||||||
@ -284,7 +311,19 @@ private extension GameCollectionViewController
|
|||||||
|
|
||||||
cell.imageView.image = #imageLiteral(resourceName: "BoxArt")
|
cell.imageView.image = #imageLiteral(resourceName: "BoxArt")
|
||||||
|
|
||||||
cell.maximumImageSize = CGSize(width: 90, height: 90)
|
if self.traitCollection.horizontalSizeClass == .regular
|
||||||
|
{
|
||||||
|
let fontDescriptor = UIFontDescriptor.preferredFontDescriptor(withTextStyle: .subheadline).withSymbolicTraits(.traitBold)!
|
||||||
|
cell.textLabel.font = UIFont(descriptor: fontDescriptor, size: 0)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cell.textLabel.font = UIFont.preferredFont(forTextStyle: .caption1)
|
||||||
|
}
|
||||||
|
|
||||||
|
let layout = self.collectionViewLayout as! GridCollectionViewLayout
|
||||||
|
cell.maximumImageSize = CGSize(width: layout.itemWidth, height: layout.itemWidth)
|
||||||
|
|
||||||
cell.textLabel.text = game.name
|
cell.textLabel.text = game.name
|
||||||
cell.textLabel.textColor = UIColor.gray
|
cell.textLabel.textColor = UIColor.gray
|
||||||
cell.tintColor = cell.textLabel.textColor
|
cell.tintColor = cell.textLabel.textColor
|
||||||
@ -484,7 +523,9 @@ private extension GameCollectionViewController
|
|||||||
|
|
||||||
func delete(_ game: Game)
|
func delete(_ game: Game)
|
||||||
{
|
{
|
||||||
let confirmationAlertController = UIAlertController(title: NSLocalizedString("Are you sure you want to delete this game? All associated data, such as saves, save states, and cheat codes, will also be deleted.", comment: ""), message: nil, preferredStyle: .actionSheet)
|
let confirmationAlertController = UIAlertController(title: NSLocalizedString("Are you sure you want to delete this game?", comment: ""),
|
||||||
|
message: NSLocalizedString("All associated data, such as saves, save states, and cheat codes, will also be deleted.", comment: ""),
|
||||||
|
preferredStyle: .alert)
|
||||||
confirmationAlertController.addAction(UIAlertAction(title: NSLocalizedString("Delete Game", comment: ""), style: .destructive, handler: { action in
|
confirmationAlertController.addAction(UIAlertAction(title: NSLocalizedString("Delete Game", comment: ""), style: .destructive, handler: { action in
|
||||||
|
|
||||||
DatabaseManager.shared.performBackgroundTask { (context) in
|
DatabaseManager.shared.performBackgroundTask { (context) in
|
||||||
@ -554,6 +595,7 @@ private extension GameCollectionViewController
|
|||||||
let importController = ImportController(documentTypes: [kUTTypeImage as String])
|
let importController = ImportController(documentTypes: [kUTTypeImage as String])
|
||||||
importController.delegate = self
|
importController.delegate = self
|
||||||
importController.importOptions = [clipboardImportOption, photoLibraryImportOption, gamesDatabaseImportOption]
|
importController.importOptions = [clipboardImportOption, photoLibraryImportOption, gamesDatabaseImportOption]
|
||||||
|
importController.sourceView = self._popoverSourceView
|
||||||
self.present(importController, animated: true, completion: nil)
|
self.present(importController, animated: true, completion: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -663,26 +705,36 @@ private extension GameCollectionViewController
|
|||||||
|
|
||||||
func share(_ game: Game)
|
func share(_ game: Game)
|
||||||
{
|
{
|
||||||
let temporaryDirectory = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
|
let temporaryDirectory = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString, isDirectory: true)
|
||||||
let symbolicURL = temporaryDirectory.appendingPathComponent(game.name + "." + game.fileURL.pathExtension)
|
|
||||||
|
let sanitizedName = game.name.components(separatedBy: .urlFilenameAllowed.inverted).joined()
|
||||||
|
let temporaryURL = temporaryDirectory.appendingPathComponent(sanitizedName + "." + game.fileURL.pathExtension, isDirectory: false)
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
try FileManager.default.createDirectory(at: temporaryDirectory, withIntermediateDirectories: true, attributes: nil)
|
try FileManager.default.createDirectory(at: temporaryDirectory, withIntermediateDirectories: true, attributes: nil)
|
||||||
|
|
||||||
// Create a symbolic link so we can control the file name used when sharing.
|
// Make a temporary copy so we can control the filename used when sharing.
|
||||||
// Otherwise, if we just passed in game.fileURL to UIActivityViewController, the file name would be the game's SHA1 hash.
|
// Otherwise, if we just passed in game.fileURL to UIActivityViewController, the file name would be the game's SHA1 hash.
|
||||||
try FileManager.default.createSymbolicLink(at: symbolicURL, withDestinationURL: game.fileURL)
|
try FileManager.default.copyItem(at: game.fileURL, to: temporaryURL, shouldReplace: true)
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
print(error)
|
let alertController = UIAlertController(title: NSLocalizedString("Could Not Share Game", comment: ""), error: error)
|
||||||
|
self.present(alertController, animated: true, completion: nil)
|
||||||
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let copyDeepLinkActivity = CopyDeepLinkActivity()
|
let copyDeepLinkActivity = CopyDeepLinkActivity()
|
||||||
|
|
||||||
let activityViewController = UIActivityViewController(activityItems: [symbolicURL, game], applicationActivities: [copyDeepLinkActivity])
|
let activityViewController = UIActivityViewController(activityItems: [temporaryURL, game], applicationActivities: [copyDeepLinkActivity])
|
||||||
|
activityViewController.popoverPresentationController?.sourceView = self._popoverSourceView?.superview
|
||||||
|
activityViewController.popoverPresentationController?.sourceRect = self._popoverSourceView?.frame ?? .zero
|
||||||
activityViewController.completionWithItemsHandler = { (activityType, finished, returnedItems, error) in
|
activityViewController.completionWithItemsHandler = { (activityType, finished, returnedItems, error) in
|
||||||
|
// Make sure the user either shared the game or cancelled before deleting temporaryDirectory.
|
||||||
|
guard finished || activityType == nil else { return }
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
try FileManager.default.removeItem(at: temporaryDirectory)
|
try FileManager.default.removeItem(at: temporaryDirectory)
|
||||||
@ -692,6 +744,7 @@ private extension GameCollectionViewController
|
|||||||
print(error)
|
print(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.present(activityViewController, animated: true, completion: nil)
|
self.present(activityViewController, animated: true, completion: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -747,8 +800,7 @@ private extension GameCollectionViewController
|
|||||||
{
|
{
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
let illegalCharacterSet = CharacterSet(charactersIn: "\"\\/?<>:*|")
|
let sanitizedFilename = game.name.components(separatedBy: .urlFilenameAllowed.inverted).joined()
|
||||||
let sanitizedFilename = game.name.components(separatedBy: illegalCharacterSet).joined() + "." + game.gameSaveURL.pathExtension
|
|
||||||
|
|
||||||
let temporaryURL = FileManager.default.temporaryDirectory.appendingPathComponent(sanitizedFilename)
|
let temporaryURL = FileManager.default.temporaryDirectory.appendingPathComponent(sanitizedFilename)
|
||||||
try FileManager.default.copyItem(at: game.gameSaveURL, to: temporaryURL, shouldReplace: true)
|
try FileManager.default.copyItem(at: game.gameSaveURL, to: temporaryURL, shouldReplace: true)
|
||||||
@ -807,6 +859,9 @@ extension GameCollectionViewController: UIViewControllerPreviewingDelegate
|
|||||||
|
|
||||||
previewingContext.sourceRect = layoutAttributes.frame
|
previewingContext.sourceRect = layoutAttributes.frame
|
||||||
|
|
||||||
|
let cell = collectionView.cellForItem(at: indexPath)
|
||||||
|
self._popoverSourceView = cell
|
||||||
|
|
||||||
let game = self.dataSource.item(at: indexPath)
|
let game = self.dataSource.item(at: indexPath)
|
||||||
|
|
||||||
let gameViewController = self.makePreviewGameViewController(for: game)
|
let gameViewController = self.makePreviewGameViewController(for: game)
|
||||||
@ -974,6 +1029,9 @@ extension GameCollectionViewController
|
|||||||
let game = self.dataSource.item(at: indexPath)
|
let game = self.dataSource.item(at: indexPath)
|
||||||
let actions = self.actions(for: game)
|
let actions = self.actions(for: game)
|
||||||
|
|
||||||
|
let cell = self.collectionView.cellForItem(at: indexPath)
|
||||||
|
self._popoverSourceView = cell
|
||||||
|
|
||||||
return UIContextMenuConfiguration(identifier: indexPath as NSIndexPath, previewProvider: { [weak self] in
|
return UIContextMenuConfiguration(identifier: indexPath as NSIndexPath, previewProvider: { [weak self] in
|
||||||
guard let self = self else { return nil }
|
guard let self = self else { return nil }
|
||||||
|
|
||||||
|
|||||||
@ -47,6 +47,7 @@ class GamesViewController: UIViewController
|
|||||||
private let fetchedResultsController: NSFetchedResultsController<NSFetchRequestResult>
|
private let fetchedResultsController: NSFetchedResultsController<NSFetchRequestResult>
|
||||||
|
|
||||||
private var searchController: RSTSearchController?
|
private var searchController: RSTSearchController?
|
||||||
|
private lazy var importController: ImportController = self.makeImportController()
|
||||||
|
|
||||||
private var syncingToastView: RSTToastView? {
|
private var syncingToastView: RSTToastView? {
|
||||||
didSet {
|
didSet {
|
||||||
@ -58,6 +59,8 @@ class GamesViewController: UIViewController
|
|||||||
}
|
}
|
||||||
private var syncingProgressObservation: NSKeyValueObservation?
|
private var syncingProgressObservation: NSKeyValueObservation?
|
||||||
|
|
||||||
|
@IBOutlet private var importButton: UIBarButtonItem!
|
||||||
|
|
||||||
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
|
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
|
||||||
fatalError("initWithNibName: not implemented")
|
fatalError("initWithNibName: not implemented")
|
||||||
}
|
}
|
||||||
@ -114,10 +117,16 @@ extension GamesViewController
|
|||||||
let navigationBarAppearance = navigationController.navigationBar.standardAppearance.copy()
|
let navigationBarAppearance = navigationController.navigationBar.standardAppearance.copy()
|
||||||
navigationBarAppearance.backgroundEffect = UIBlurEffect(style: .dark)
|
navigationBarAppearance.backgroundEffect = UIBlurEffect(style: .dark)
|
||||||
navigationController.navigationBar.standardAppearance = navigationBarAppearance
|
navigationController.navigationBar.standardAppearance = navigationBarAppearance
|
||||||
|
navigationController.navigationBar.scrollEdgeAppearance = navigationBarAppearance
|
||||||
|
|
||||||
let toolbarAppearance = navigationController.toolbar.standardAppearance.copy()
|
let toolbarAppearance = navigationController.toolbar.standardAppearance.copy()
|
||||||
toolbarAppearance.backgroundEffect = UIBlurEffect(style: .dark)
|
toolbarAppearance.backgroundEffect = UIBlurEffect(style: .dark)
|
||||||
navigationController.toolbar.standardAppearance = toolbarAppearance
|
navigationController.toolbar.standardAppearance = toolbarAppearance
|
||||||
|
|
||||||
|
if #available(iOS 15, *)
|
||||||
|
{
|
||||||
|
navigationController.toolbar.scrollEdgeAppearance = toolbarAppearance
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -126,6 +135,22 @@ extension GamesViewController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if #available(iOS 14, *)
|
||||||
|
{
|
||||||
|
self.importController.presentingViewController = self
|
||||||
|
|
||||||
|
let importActions = self.importController.makeActions().menuActions
|
||||||
|
let importMenu = UIMenu(title: NSLocalizedString("Import From…", comment: ""), image: UIImage(systemName: "square.and.arrow.down"), children: importActions)
|
||||||
|
self.importButton.menu = importMenu
|
||||||
|
|
||||||
|
self.importButton.action = nil
|
||||||
|
self.importButton.target = nil
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
self.importController.barButtonItem = self.importButton
|
||||||
|
}
|
||||||
|
|
||||||
self.prepareSearchController()
|
self.prepareSearchController()
|
||||||
|
|
||||||
self.updateTheme()
|
self.updateTheme()
|
||||||
@ -352,7 +377,7 @@ private extension GamesViewController
|
|||||||
/// Importing
|
/// Importing
|
||||||
extension GamesViewController: ImportControllerDelegate
|
extension GamesViewController: ImportControllerDelegate
|
||||||
{
|
{
|
||||||
@IBAction private func importFiles()
|
private func makeImportController() -> ImportController
|
||||||
{
|
{
|
||||||
var documentTypes = Set(System.registeredSystems.map { $0.gameType.rawValue })
|
var documentTypes = Set(System.registeredSystems.map { $0.gameType.rawValue })
|
||||||
documentTypes.insert(kUTTypeZipArchive as String)
|
documentTypes.insert(kUTTypeZipArchive as String)
|
||||||
@ -373,7 +398,13 @@ extension GamesViewController: ImportControllerDelegate
|
|||||||
let importController = ImportController(documentTypes: documentTypes)
|
let importController = ImportController(documentTypes: documentTypes)
|
||||||
importController.delegate = self
|
importController.delegate = self
|
||||||
importController.importOptions = [itunesImportOption]
|
importController.importOptions = [itunesImportOption]
|
||||||
self.present(importController, animated: true, completion: nil)
|
|
||||||
|
return importController
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction private func importFiles()
|
||||||
|
{
|
||||||
|
self.present(self.importController, animated: true, completion: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func importController(_ importController: ImportController, didImportItemsAt urls: Set<URL>, errors: [Error])
|
func importController(_ importController: ImportController, didImportItemsAt urls: Set<URL>, errors: [Error])
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import DeltaCore
|
|||||||
struct iTunesImportOption: ImportOption
|
struct iTunesImportOption: ImportOption
|
||||||
{
|
{
|
||||||
let title = NSLocalizedString("iTunes", comment: "")
|
let title = NSLocalizedString("iTunes", comment: "")
|
||||||
let image: UIImage? = nil
|
let image: UIImage? = UIImage(symbolNameIfAvailable: "music.note")
|
||||||
|
|
||||||
private let presentingViewController: UIViewController
|
private let presentingViewController: UIViewController
|
||||||
|
|
||||||
|
|||||||
@ -37,7 +37,10 @@ class ImportController: NSObject
|
|||||||
var delegate: ImportControllerDelegate?
|
var delegate: ImportControllerDelegate?
|
||||||
var importOptions: [ImportOption]?
|
var importOptions: [ImportOption]?
|
||||||
|
|
||||||
private weak var presentingViewController: UIViewController?
|
weak var presentingViewController: UIViewController?
|
||||||
|
|
||||||
|
weak var barButtonItem: UIBarButtonItem?
|
||||||
|
weak var sourceView: UIView?
|
||||||
|
|
||||||
// Store presentedViewController separately, since when we dismiss we don't know if it has already been dismissed.
|
// Store presentedViewController separately, since when we dismiss we don't know if it has already been dismissed.
|
||||||
// Calling dismiss on presentingViewController in that case would dismiss presentingViewController, which is bad.
|
// Calling dismiss on presentingViewController in that case would dismiss presentingViewController, which is bad.
|
||||||
@ -61,26 +64,54 @@ class ImportController: NSObject
|
|||||||
super.init()
|
super.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func makeActions() -> [Action]
|
||||||
|
{
|
||||||
|
assert(self.presentingViewController != nil, "presentingViewController must be set before calling makeActions()")
|
||||||
|
|
||||||
|
var actions = (self.importOptions ?? []).map { (option) -> Action in
|
||||||
|
let action = Action(title: option.title, style: .default, image: option.image) { _ in
|
||||||
|
option.import { importedURLs in
|
||||||
|
self.finish(with: importedURLs, errors: [])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return action
|
||||||
|
}
|
||||||
|
|
||||||
|
let filesAction = Action(title: NSLocalizedString("Files", comment: ""), style: .default, image: UIImage(symbolNameIfAvailable: "doc")) { action in
|
||||||
|
self.presentDocumentBrowser()
|
||||||
|
}
|
||||||
|
actions.append(filesAction)
|
||||||
|
|
||||||
|
return actions
|
||||||
|
}
|
||||||
|
|
||||||
fileprivate func presentImportController(from presentingViewController: UIViewController, animated: Bool, completionHandler: (() -> Void)?)
|
fileprivate func presentImportController(from presentingViewController: UIViewController, animated: Bool, completionHandler: (() -> Void)?)
|
||||||
{
|
{
|
||||||
self.presentingViewController = presentingViewController
|
self.presentingViewController = presentingViewController
|
||||||
|
|
||||||
|
let actions = self.makeActions()
|
||||||
|
|
||||||
|
if actions.count > 1
|
||||||
|
{
|
||||||
let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
|
let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
|
||||||
alertController.addAction(UIAlertAction.cancel)
|
alertController.addAction(UIAlertAction.cancel)
|
||||||
|
|
||||||
if let importOptions = self.importOptions
|
let alertActions = actions.map { UIAlertAction($0) }
|
||||||
|
for action in alertActions
|
||||||
{
|
{
|
||||||
for importOption in importOptions
|
alertController.addAction(action)
|
||||||
{
|
|
||||||
alertController.add(importOption) { [unowned self] (urls) in
|
|
||||||
self.finish(with: urls, errors: [])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let filesAction = UIAlertAction(title: NSLocalizedString("Files", comment: ""), style: .default) { (action) in
|
if let sourceView = self.sourceView
|
||||||
self.presentDocumentBrowser()
|
{
|
||||||
|
alertController.popoverPresentationController?.sourceView = sourceView.superview
|
||||||
|
alertController.popoverPresentationController?.sourceRect = sourceView.frame
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
alertController.popoverPresentationController?.barButtonItem = self.barButtonItem
|
||||||
}
|
}
|
||||||
alertController.addAction(filesAction)
|
|
||||||
|
|
||||||
self.presentedViewController = alertController
|
self.presentedViewController = alertController
|
||||||
self.presentingViewController?.present(alertController, animated: true, completion: nil)
|
self.presentingViewController?.present(alertController, animated: true, completion: nil)
|
||||||
@ -198,7 +229,7 @@ private var ImportControllerKey: UInt8 = 0
|
|||||||
|
|
||||||
extension UIViewController
|
extension UIViewController
|
||||||
{
|
{
|
||||||
fileprivate(set) var importController: ImportController?
|
fileprivate var importController: ImportController?
|
||||||
{
|
{
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
|
|||||||
@ -93,14 +93,6 @@ extension SaveStatesViewController
|
|||||||
self.collectionView?.dataSource = self.dataSource
|
self.collectionView?.dataSource = self.dataSource
|
||||||
self.collectionView?.prefetchDataSource = self.dataSource
|
self.collectionView?.prefetchDataSource = self.dataSource
|
||||||
|
|
||||||
let collectionViewLayout = self.collectionViewLayout as! GridCollectionViewLayout
|
|
||||||
let averageHorizontalInset = (collectionViewLayout.sectionInset.left + collectionViewLayout.sectionInset.right) / 2
|
|
||||||
let portraitScreenWidth = UIScreen.main.coordinateSpace.convert(UIScreen.main.bounds, to: UIScreen.main.fixedCoordinateSpace).width
|
|
||||||
|
|
||||||
// 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 = floor((portraitScreenWidth - (averageHorizontalInset * 3)) / 2)
|
|
||||||
|
|
||||||
switch self.mode
|
switch self.mode
|
||||||
{
|
{
|
||||||
case .saving:
|
case .saving:
|
||||||
@ -113,8 +105,7 @@ extension SaveStatesViewController
|
|||||||
self.navigationItem.rightBarButtonItems?.removeFirst()
|
self.navigationItem.rightBarButtonItems?.removeFirst()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Manually update prototype cell properties
|
self.prototypeCellWidthConstraint = self.prototypeCell.contentView.widthAnchor.constraint(equalToConstant: 0)
|
||||||
self.prototypeCellWidthConstraint = self.prototypeCell.contentView.widthAnchor.constraint(equalToConstant: collectionViewLayout.itemWidth)
|
|
||||||
self.prototypeCellWidthConstraint.isActive = true
|
self.prototypeCellWidthConstraint.isActive = true
|
||||||
|
|
||||||
self.prepareEmulatorCoreSaveState()
|
self.prepareEmulatorCoreSaveState()
|
||||||
@ -238,6 +229,26 @@ private extension SaveStatesViewController
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.sortButton.transform = CGAffineTransform.identity.rotated(by: Settings.sortSaveStatesByOldestFirst ? 0 : .pi)
|
self.sortButton.transform = CGAffineTransform.identity.rotated(by: Settings.sortSaveStatesByOldestFirst ? 0 : .pi)
|
||||||
|
|
||||||
|
let collectionViewLayout = self.collectionViewLayout as! GridCollectionViewLayout
|
||||||
|
|
||||||
|
if self.traitCollection.horizontalSizeClass == .regular
|
||||||
|
{
|
||||||
|
collectionViewLayout.itemWidth = 180
|
||||||
|
collectionViewLayout.minimumInteritemSpacing = 30
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
let averageHorizontalInset = (collectionViewLayout.sectionInset.left + collectionViewLayout.sectionInset.right) / 2
|
||||||
|
let portraitScreenWidth = UIScreen.main.coordinateSpace.convert(UIScreen.main.bounds, to: UIScreen.main.fixedCoordinateSpace).width
|
||||||
|
|
||||||
|
// 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 = floor((portraitScreenWidth - (averageHorizontalInset * 3)) / 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Manually update prototype cell properties
|
||||||
|
self.prototypeCellWidthConstraint.constant = collectionViewLayout.itemWidth
|
||||||
}
|
}
|
||||||
|
|
||||||
//MARK: - Configure Views -
|
//MARK: - Configure Views -
|
||||||
|
|||||||
@ -40,6 +40,8 @@ class ControllerInputsViewController: UIViewController
|
|||||||
|
|
||||||
private var activeCalloutView: InputCalloutView?
|
private var activeCalloutView: InputCalloutView?
|
||||||
|
|
||||||
|
private var _didLayoutSubviews = false
|
||||||
|
|
||||||
@IBOutlet private var actionsMenuViewControllerHeightConstraint: NSLayoutConstraint!
|
@IBOutlet private var actionsMenuViewControllerHeightConstraint: NSLayoutConstraint!
|
||||||
@IBOutlet private var cancelTapGestureRecognizer: UITapGestureRecognizer!
|
@IBOutlet private var cancelTapGestureRecognizer: UITapGestureRecognizer!
|
||||||
|
|
||||||
@ -65,7 +67,15 @@ class ControllerInputsViewController: UIViewController
|
|||||||
|
|
||||||
self.gameViewController.controllerView.addReceiver(self)
|
self.gameViewController.controllerView.addReceiver(self)
|
||||||
|
|
||||||
|
if let navigationController = self.navigationController, #available(iOS 13, *)
|
||||||
|
{
|
||||||
|
navigationController.overrideUserInterfaceStyle = .dark
|
||||||
|
navigationController.navigationBar.scrollEdgeAppearance = navigationController.navigationBar.standardAppearance // Fixes invisible navigation bar on iPad.
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
self.navigationController?.navigationBar.barStyle = .black
|
self.navigationController?.navigationBar.barStyle = .black
|
||||||
|
}
|
||||||
|
|
||||||
NSLayoutConstraint.activate([self.gameViewController.gameView.centerYAnchor.constraint(equalTo: self.actionsMenuViewController.view.centerYAnchor)])
|
NSLayoutConstraint.activate([self.gameViewController.gameView.centerYAnchor.constraint(equalTo: self.actionsMenuViewController.view.centerYAnchor)])
|
||||||
|
|
||||||
@ -81,6 +91,23 @@ class ControllerInputsViewController: UIViewController
|
|||||||
{
|
{
|
||||||
self.actionsMenuViewControllerHeightConstraint.constant = self.actionsMenuViewController.preferredContentSize.height
|
self.actionsMenuViewControllerHeightConstraint.constant = self.actionsMenuViewController.preferredContentSize.height
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let window = self.view.window, !_didLayoutSubviews
|
||||||
|
{
|
||||||
|
var traits = DeltaCore.ControllerSkin.Traits.defaults(for: window)
|
||||||
|
traits.orientation = .portrait
|
||||||
|
|
||||||
|
if traits.device == .ipad
|
||||||
|
{
|
||||||
|
// Use standard iPhone skins instead of iPad skins.
|
||||||
|
traits.device = .iphone
|
||||||
|
traits.displayType = .standard
|
||||||
|
}
|
||||||
|
|
||||||
|
self.gameViewController.controllerView.overrideControllerSkinTraits = traits
|
||||||
|
|
||||||
|
_didLayoutSubviews = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewDidAppear(_ animated: Bool)
|
override func viewDidAppear(_ animated: Bool)
|
||||||
@ -403,6 +430,7 @@ private extension ControllerInputsViewController
|
|||||||
}
|
}
|
||||||
|
|
||||||
let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
|
let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
|
||||||
|
alertController.popoverPresentationController?.barButtonItem = sender
|
||||||
alertController.addAction(.cancel)
|
alertController.addAction(.cancel)
|
||||||
alertController.addAction(UIAlertAction(title: NSLocalizedString("Reset Controls to Defaults", comment: ""), style: .destructive, handler: { (action) in
|
alertController.addAction(UIAlertAction(title: NSLocalizedString("Reset Controls to Defaults", comment: ""), style: .destructive, handler: { (action) in
|
||||||
reset()
|
reset()
|
||||||
@ -562,3 +590,15 @@ extension ControllerInputsViewController: SMCalloutViewDelegate
|
|||||||
self.toggle(calloutView)
|
self.toggle(calloutView)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension ControllerInputsViewController: UIAdaptivePresentationControllerDelegate
|
||||||
|
{
|
||||||
|
func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle
|
||||||
|
{
|
||||||
|
switch (traitCollection.horizontalSizeClass, traitCollection.verticalSizeClass)
|
||||||
|
{
|
||||||
|
case (.regular, .regular): return .formSheet // Regular width and height, so display as form sheet
|
||||||
|
default: return .fullScreen // Compact width and/or height, so display full screen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -108,9 +108,18 @@ extension ControllersSettingsViewController
|
|||||||
switch identifier
|
switch identifier
|
||||||
{
|
{
|
||||||
case "controllerInputsSegue":
|
case "controllerInputsSegue":
|
||||||
let controllerInputsViewController = (segue.destination as! UINavigationController).topViewController as! ControllerInputsViewController
|
let navigationController = segue.destination as! UINavigationController
|
||||||
|
|
||||||
|
let controllerInputsViewController = navigationController.topViewController as! ControllerInputsViewController
|
||||||
controllerInputsViewController.gameController = self.gameController
|
controllerInputsViewController.gameController = self.gameController
|
||||||
|
|
||||||
|
if self.view.traitCollection.userInterfaceIdiom == .pad
|
||||||
|
{
|
||||||
|
// For now, only iPads can display ControllerInputsViewController as a form sheet.
|
||||||
|
navigationController.modalPresentationStyle = .formSheet
|
||||||
|
navigationController.presentationController?.delegate = controllerInputsViewController
|
||||||
|
}
|
||||||
|
|
||||||
default: break
|
default: break
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -296,6 +305,8 @@ extension ControllersSettingsViewController
|
|||||||
{
|
{
|
||||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
|
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
|
||||||
{
|
{
|
||||||
|
let previousGameController = self.gameController
|
||||||
|
|
||||||
switch Section(rawValue: indexPath.section)!
|
switch Section(rawValue: indexPath.section)!
|
||||||
{
|
{
|
||||||
case .localDevice: self.gameController = self.localDeviceController
|
case .localDevice: self.gameController = self.localDeviceController
|
||||||
@ -310,7 +321,7 @@ extension ControllersSettingsViewController
|
|||||||
|
|
||||||
let previousIndexPath: IndexPath?
|
let previousIndexPath: IndexPath?
|
||||||
|
|
||||||
if let gameController = self.gameController
|
if let gameController = previousGameController
|
||||||
{
|
{
|
||||||
if gameController == self.localDeviceController
|
if gameController == self.localDeviceController
|
||||||
{
|
{
|
||||||
|
|||||||
@ -215,6 +215,8 @@
|
|||||||
<array>
|
<array>
|
||||||
<string>armv7</string>
|
<string>armv7</string>
|
||||||
</array>
|
</array>
|
||||||
|
<key>UIRequiresFullScreen</key>
|
||||||
|
<true/>
|
||||||
<key>UIStatusBarStyle</key>
|
<key>UIStatusBarStyle</key>
|
||||||
<string>UIStatusBarStyleLightContent</string>
|
<string>UIStatusBarStyleLightContent</string>
|
||||||
<key>UISupportedInterfaceOrientations</key>
|
<key>UISupportedInterfaceOrientations</key>
|
||||||
@ -223,6 +225,13 @@
|
|||||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||||
</array>
|
</array>
|
||||||
|
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||||
|
<array>
|
||||||
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
|
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||||
|
</array>
|
||||||
<key>UISupportsDocumentBrowser</key>
|
<key>UISupportsDocumentBrowser</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>UTExportedTypeDeclarations</key>
|
<key>UTExportedTypeDeclarations</key>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user