Merge branch 'feature/ios11' into develop

This commit is contained in:
Riley Testut 2017-10-12 10:45:54 -07:00
commit c16562c8ca
7 changed files with 199 additions and 63 deletions

View File

@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12120" systemVersion="16E195" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="SPq-Bk-fQl"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13196" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="SPq-Bk-fQl">
<device id="retina4_7" orientation="portrait"> <device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/> <adaptation id="fullscreen"/>
</device> </device>
<dependencies> <dependencies>
<deployment identifier="iOS"/> <deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12088"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13173"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
<scenes> <scenes>
@ -31,8 +31,8 @@
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints> <constraints>
<constraint firstAttribute="trailing" secondItem="tmn-gd-5UN" secondAttribute="trailing" id="9Rq-HM-vqk"/> <constraint firstAttribute="trailing" secondItem="tmn-gd-5UN" secondAttribute="trailing" id="9Rq-HM-vqk"/>
<constraint firstItem="0om-QB-N5a" firstAttribute="top" secondItem="tmn-gd-5UN" secondAttribute="bottom" id="DV5-hh-1VN"/>
<constraint firstItem="tmn-gd-5UN" firstAttribute="leading" secondItem="3Bk-k3-7J9" secondAttribute="leading" id="f1f-sa-dBA"/> <constraint firstItem="tmn-gd-5UN" firstAttribute="leading" secondItem="3Bk-k3-7J9" secondAttribute="leading" id="f1f-sa-dBA"/>
<constraint firstAttribute="bottom" secondItem="tmn-gd-5UN" secondAttribute="bottom" id="ifM-Wa-u9y"/>
<constraint firstItem="tmn-gd-5UN" firstAttribute="top" secondItem="3Bk-k3-7J9" secondAttribute="top" id="nhS-aC-rUR"/> <constraint firstItem="tmn-gd-5UN" firstAttribute="top" secondItem="3Bk-k3-7J9" secondAttribute="top" id="nhS-aC-rUR"/>
</constraints> </constraints>
</view> </view>
@ -57,7 +57,7 @@
<scene sceneID="qNA-NP-TiF"> <scene sceneID="qNA-NP-TiF">
<objects> <objects>
<collectionViewController storyboardIdentifier="gameCollectionViewController" clearsSelectionOnViewWillAppear="NO" id="kqu-75-owz" customClass="GameCollectionViewController" customModule="Delta" customModuleProvider="target" sceneMemberID="viewController"> <collectionViewController storyboardIdentifier="gameCollectionViewController" clearsSelectionOnViewWillAppear="NO" id="kqu-75-owz" customClass="GameCollectionViewController" customModule="Delta" customModuleProvider="target" sceneMemberID="viewController">
<collectionView key="view" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" id="OIq-Z8-kxO"> <collectionView key="view" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" indicatorStyle="white" dataMode="prototypes" id="OIq-Z8-kxO">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/> <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
@ -183,12 +183,11 @@
<toolbarItems/> <toolbarItems/>
<nil key="simulatedBottomBarMetrics"/> <nil key="simulatedBottomBarMetrics"/>
<navigationBar key="navigationBar" contentMode="scaleToFill" id="wj9-1e-eev"> <navigationBar key="navigationBar" contentMode="scaleToFill" id="wj9-1e-eev">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/> <rect key="frame" x="0.0" y="20" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
</navigationBar> </navigationBar>
<nil name="viewControllers"/> <nil name="viewControllers"/>
<toolbar key="toolbar" opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="M4r-sO-G4H"> <toolbar key="toolbar" opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="M4r-sO-G4H">
<rect key="frame" x="0.0" y="556" width="600" height="44"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
</toolbar> </toolbar>
<connections> <connections>
@ -213,7 +212,7 @@
<navigationController storyboardIdentifier="saveStatesNavigationController" automaticallyAdjustsScrollViewInsets="NO" id="MPk-bF-nkj" sceneMemberID="viewController"> <navigationController storyboardIdentifier="saveStatesNavigationController" automaticallyAdjustsScrollViewInsets="NO" id="MPk-bF-nkj" sceneMemberID="viewController">
<toolbarItems/> <toolbarItems/>
<navigationBar key="navigationBar" contentMode="scaleToFill" id="twH-3X-6DV"> <navigationBar key="navigationBar" contentMode="scaleToFill" id="twH-3X-6DV">
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/> <rect key="frame" x="0.0" y="20" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
</navigationBar> </navigationBar>
<nil name="viewControllers"/> <nil name="viewControllers"/>

View File

@ -504,10 +504,12 @@ extension GameCollectionViewController: SaveStatesViewControllerDelegate
/// ImportControllerDelegate /// ImportControllerDelegate
extension GameCollectionViewController: ImportControllerDelegate extension GameCollectionViewController: ImportControllerDelegate
{ {
func importController(_ importController: ImportController, didImportItemsAt urls: Set<URL>) func importController(_ importController: ImportController, didImportItemsAt urls: Set<URL>, errors: [Error])
{ {
guard let game = self._changingArtworkGame else { return } guard let game = self._changingArtworkGame else { return }
var errors = errors
var imageURL: URL? var imageURL: URL?
if let url = urls.first if let url = urls.first
@ -533,7 +535,7 @@ extension GameCollectionViewController: ImportControllerDelegate
} }
catch catch
{ {
print(error) errors.append(error)
} }
} }
else else
@ -542,6 +544,11 @@ extension GameCollectionViewController: ImportControllerDelegate
} }
} }
for error in errors
{
print(error)
}
if let imageURL = imageURL if let imageURL = imageURL
{ {
// Remove previous artwork from cache. // Remove previous artwork from cache.

View File

@ -109,6 +109,9 @@ extension GamesViewController
{ {
super.viewDidLayoutSubviews() super.viewDidLayoutSubviews()
if #available(iOS 11.0, *) {}
else
{
if let viewControllers = self.pageViewController.viewControllers as? [GameCollectionViewController] if let viewControllers = self.pageViewController.viewControllers as? [GameCollectionViewController]
{ {
for viewController in viewControllers for viewController in viewControllers
@ -117,6 +120,7 @@ extension GamesViewController
} }
} }
} }
}
override func didReceiveMemoryWarning() override func didReceiveMemoryWarning()
{ {
@ -190,8 +194,12 @@ private extension GamesViewController
viewController.theme = self.theme viewController.theme = self.theme
viewController.activeEmulatorCore = self.activeEmulatorCore viewController.activeEmulatorCore = self.activeEmulatorCore
if #available(iOS 11.0, *) {}
else
{
// Need to set content inset here AND willTransitionTo callback to ensure its correct for all edge cases // Need to set content inset here AND willTransitionTo callback to ensure its correct for all edge cases
viewController.collectionView?.contentInset.top = self.topLayoutGuide.length viewController.collectionView?.contentInset.top = self.topLayoutGuide.length
}
return viewController return viewController
} }
@ -283,8 +291,13 @@ extension GamesViewController: ImportControllerDelegate
self.present(importController, animated: true, completion: nil) self.present(importController, animated: true, completion: nil)
} }
func importController(_ importController: ImportController, didImportItemsAt urls: Set<URL>) func importController(_ importController: ImportController, didImportItemsAt urls: Set<URL>, errors: [Error])
{ {
for error in errors
{
print(error)
}
let gameURLs = urls.filter { $0.pathExtension.lowercased() != "deltaskin" } let gameURLs = urls.filter { $0.pathExtension.lowercased() != "deltaskin" }
DatabaseManager.shared.importGames(at: Set(gameURLs)) { (games, errors) in DatabaseManager.shared.importGames(at: Set(gameURLs)) { (games, errors) in
if errors.count > 0 if errors.count > 0
@ -361,11 +374,15 @@ extension GamesViewController: UIPageViewControllerDataSource, UIPageViewControl
{ {
guard let viewControllers = pendingViewControllers as? [GameCollectionViewController] else { return } guard let viewControllers = pendingViewControllers as? [GameCollectionViewController] else { return }
if #available(iOS 11.0, *) {}
else
{
for viewController in viewControllers for viewController in viewControllers
{ {
viewController.collectionView?.contentInset.top = self.topLayoutGuide.length viewController.collectionView?.contentInset.top = self.topLayoutGuide.length
} }
} }
}
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool)
{ {

View File

@ -16,7 +16,7 @@ import Roxas
protocol ImportControllerDelegate protocol ImportControllerDelegate
{ {
func importController(_ importController: ImportController, didImportItemsAt urls: Set<URL>) func importController(_ importController: ImportController, didImportItemsAt urls: Set<URL>, errors: [Error])
/** Optional **/ /** Optional **/
func importControllerDidCancel(_ importController: ImportController) func importControllerDidCancel(_ importController: ImportController)
@ -37,20 +37,35 @@ class ImportController: NSObject
var delegate: ImportControllerDelegate? var delegate: ImportControllerDelegate?
var importOptions: [ImportOption]? var importOptions: [ImportOption]?
fileprivate weak var presentingViewController: UIViewController?
// 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.
fileprivate weak var presentedViewController: UIViewController?
fileprivate let importQueue: OperationQueue
fileprivate let fileCoordinator: NSFileCoordinator
init(documentTypes: Set<String>) init(documentTypes: Set<String>)
{ {
self.documentTypes = documentTypes self.documentTypes = documentTypes
let dispatchQueue = DispatchQueue(label: "com.rileytestut.Delta.ImportController.dispatchQueue", qos: .userInitiated, attributes: .concurrent)
self.importQueue = OperationQueue()
self.importQueue.name = "com.rileytestut.Delta.ImportController.importQueue"
self.importQueue.underlyingQueue = dispatchQueue
self.fileCoordinator = NSFileCoordinator(filePresenter: nil)
super.init() super.init()
} }
fileprivate weak var presentingViewController: UIViewController?
fileprivate func presentImportController(from presentingViewController: UIViewController, animated: Bool, completionHandler: ((Void) -> Void)?) fileprivate func presentImportController(from presentingViewController: UIViewController, animated: Bool, completionHandler: ((Void) -> Void)?)
{ {
self.presentingViewController = presentingViewController self.presentingViewController = presentingViewController
#if IMPACTOR #if IMPACTOR
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)
@ -59,13 +74,16 @@ class ImportController: NSObject
{ {
for importOption in importOptions for importOption in importOptions
{ {
alertController.add(importOption, completionHandler: finish(with:)) alertController.add(importOption) { [unowned self] (urls) in
self.finish(with: urls, errors: [])
}
} }
} }
self.presentedViewController = alertController
self.presentingViewController?.present(alertController, animated: true, completion: nil) self.presentingViewController?.present(alertController, animated: true, completion: nil)
#else #else
let documentMenuController = UIDocumentMenuViewController(documentTypes: Array(self.documentTypes), in: .import) let documentMenuController = UIDocumentMenuViewController(documentTypes: Array(self.documentTypes), in: .import)
documentMenuController.delegate = self documentMenuController.delegate = self
@ -74,26 +92,36 @@ class ImportController: NSObject
{ {
for importOption in reversedImportOptions for importOption in reversedImportOptions
{ {
documentMenuController.add(importOption, order: .first, completionHandler: finish(with:)) documentMenuController.add(importOption, order: .first) { [unowned self] (urls) in
self.finish(with: urls, errors: [])
}
} }
} }
self.presentedViewController = documentMenuController
self.presentingViewController?.present(documentMenuController, animated: true, completion: nil) self.presentingViewController?.present(documentMenuController, animated: true, completion: nil)
#endif
#endif
} }
fileprivate func finish(with urls: Set<URL>?) @objc fileprivate func cancel()
{
self.finish(with: nil, errors: [])
}
fileprivate func finish(with urls: Set<URL>?, errors: [Error])
{ {
if let urls = urls if let urls = urls
{ {
self.delegate?.importController(self, didImportItemsAt: urls) self.delegate?.importController(self, didImportItemsAt: urls, errors: errors)
} }
else else
{ {
self.delegate?.importControllerDidCancel(self) self.delegate?.importControllerDidCancel(self)
} }
self.presentedViewController?.dismiss(animated: true)
self.presentingViewController?.importController = nil self.presentingViewController?.importController = nil
} }
} }
@ -102,14 +130,33 @@ class ImportController: NSObject
extension ImportController: UIDocumentMenuDelegate extension ImportController: UIDocumentMenuDelegate
{ {
func documentMenu(_ documentMenu: UIDocumentMenuViewController, didPickDocumentPicker documentPicker: UIDocumentPickerViewController) func documentMenu(_ documentMenu: UIDocumentMenuViewController, didPickDocumentPicker documentPicker: UIDocumentPickerViewController)
{
if #available(iOS 11.0, *)
{
let cancelButton = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(ImportController.cancel))
let documentBrowserViewController = UIDocumentBrowserViewController(forOpeningFilesWithContentTypes: Array(self.documentTypes))
documentBrowserViewController.delegate = self
documentBrowserViewController.browserUserInterfaceStyle = .dark
documentBrowserViewController.allowsPickingMultipleItems = true
documentBrowserViewController.allowsDocumentCreation = false
documentBrowserViewController.additionalTrailingNavigationBarButtonItems = [cancelButton]
self.presentedViewController = documentBrowserViewController
self.presentingViewController?.present(documentBrowserViewController, animated: true, completion: nil)
}
else
{ {
documentPicker.delegate = self documentPicker.delegate = self
self.presentedViewController = documentPicker
self.presentingViewController?.present(documentPicker, animated: true, completion: nil) self.presentingViewController?.present(documentPicker, animated: true, completion: nil)
} }
}
func documentMenuWasCancelled(_ documentMenu: UIDocumentMenuViewController) func documentMenuWasCancelled(_ documentMenu: UIDocumentMenuViewController)
{ {
self.finish(with: nil) self.finish(with: nil, errors: [])
} }
} }
@ -117,15 +164,66 @@ extension ImportController: UIDocumentPickerDelegate
{ {
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentAt url: URL) func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentAt url: URL)
{ {
self.finish(with: [url]) self.finish(with: [url], errors: [])
}
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL])
{
self.finish(with: Set(urls), errors: [])
} }
func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController)
{ {
self.finish(with: nil) self.finish(with: nil, errors: [])
} }
} }
@available(iOS 11.0, *)
extension ImportController: UIDocumentBrowserViewControllerDelegate
{
func documentBrowser(_ controller: UIDocumentBrowserViewController, didPickDocumentURLs documentURLs: [URL])
{
var coordinatedURLs = Set<URL>()
var errors = [Error]()
let dispatchGroup = DispatchGroup()
for url in documentURLs
{
dispatchGroup.enter()
let intent = NSFileAccessIntent.readingIntent(with: url)
self.fileCoordinator.coordinate(with: [intent], queue: self.importQueue) { (error) in
if let error = error
{
errors.append(error)
}
else
{
let temporaryURL = FileManager.default.temporaryDirectory.appendingPathComponent(url.lastPathComponent)
do
{
// Always access intent.url, as the system may have updated it when requesting access.
try FileManager.default.copyItem(at: intent.url, to: temporaryURL)
coordinatedURLs.insert(temporaryURL)
}
catch
{
errors.append(error)
}
}
dispatchGroup.leave()
}
}
dispatchGroup.notify(queue: self.importQueue.underlyingQueue!) {
self.finish(with: coordinatedURLs, errors: errors)
}
}
}
private var ImportControllerKey: UInt8 = 0 private var ImportControllerKey: UInt8 = 0

View File

@ -17,13 +17,17 @@ protocol PauseInfoProviding
class PausePresentationController: UIPresentationController class PausePresentationController: UIPresentationController
{ {
let presentationAnimator: UIViewPropertyAnimator
private let blurringView: UIVisualEffectView private let blurringView: UIVisualEffectView
private let vibrancyView: UIVisualEffectView private let vibrancyView: UIVisualEffectView
private var contentView: UIView! private var contentView: UIView!
@IBOutlet private weak var pauseLabel: UILabel!
@IBOutlet private weak var pauseIconImageView: UIImageView! // Must not be weak, or else may result in crash when deallocating.
@IBOutlet private weak var stackView: UIStackView! @IBOutlet private var pauseLabel: UILabel!
@IBOutlet private var pauseIconImageView: UIImageView!
@IBOutlet private var stackView: UIStackView!
override var frameOfPresentedViewInContainerView: CGRect override var frameOfPresentedViewInContainerView: CGRect
{ {
@ -45,8 +49,10 @@ class PausePresentationController: UIPresentationController
return frame return frame
} }
override init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?) init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?, presentationAnimator: UIViewPropertyAnimator)
{ {
self.presentationAnimator = presentationAnimator
self.blurringView = UIVisualEffectView(effect: nil) self.blurringView = UIVisualEffectView(effect: nil)
self.vibrancyView = UIVisualEffectView(effect: nil) self.vibrancyView = UIVisualEffectView(effect: nil)
@ -83,16 +89,18 @@ class PausePresentationController: UIPresentationController
self.contentView.alpha = 0.0 self.contentView.alpha = 0.0
self.vibrancyView.contentView.addSubview(self.contentView) self.vibrancyView.contentView.addSubview(self.contentView)
self.presentingViewController.transitionCoordinator?.animate(alongsideTransition: { context in self.presentationAnimator.addAnimations {
let blurEffect = UIBlurEffect(style: .dark) let blurEffect = UIBlurEffect(style: .dark)
self.blurringView.effect = blurEffect self.blurringView.effect = blurEffect
self.vibrancyView.effect = UIVibrancyEffect(blurEffect: blurEffect) self.vibrancyView.effect = UIVibrancyEffect(blurEffect: blurEffect)
self.contentView.alpha = 1.0 self.contentView.alpha = 1.0
}
}, completion: nil) // I have absolutely no clue why animating with transition coordinator results in no animation on iOS 11.
// Spent far too long trying to fix it, so just use the presentation animator.
// self.presentingViewController.transitionCoordinator?.animate(alongsideTransition: { context in }, completion: nil)
} }
override func dismissalTransitionWillBegin() override func dismissalTransitionWillBegin()

View File

@ -18,7 +18,7 @@ class PauseStoryboardSegue: UIStoryboardSegue
let timingParameters = UISpringTimingParameters(mass: 3.0, stiffness: 750, damping: 65, initialVelocity: CGVector(dx: 0, dy: 0)) let timingParameters = UISpringTimingParameters(mass: 3.0, stiffness: 750, damping: 65, initialVelocity: CGVector(dx: 0, dy: 0))
self.animator = UIViewPropertyAnimator(duration: 0, timingParameters: timingParameters) self.animator = UIViewPropertyAnimator(duration: 0, timingParameters: timingParameters)
self.presentationController = PausePresentationController(presentedViewController: destination, presenting: source) self.presentationController = PausePresentationController(presentedViewController: destination, presenting: source, presentationAnimator: self.animator)
super.init(identifier: identifier, source: source, destination: destination) super.init(identifier: identifier, source: source, destination: destination)
} }

View File

@ -37,6 +37,13 @@ class PauseTransitionCoordinator: NSObject, UIViewControllerAnimatedTransitionin
destinationViewController.view.layoutIfNeeded() destinationViewController.view.layoutIfNeeded()
if let navigationController = destinationViewController.navigationController
{
// Layout before animation to prevent strange bar button item layout during animation.
navigationController.view.setNeedsLayout()
navigationController.view.layoutIfNeeded()
}
UIView.animate(withDuration: self.transitionDuration(using: transitionContext), delay:0, options:RSTSystemTransitionAnimationCurve, animations: { UIView.animate(withDuration: self.transitionDuration(using: transitionContext), delay:0, options:RSTSystemTransitionAnimationCurve, animations: {
sourceViewController.view.frame.origin.y = self.presenting ? -sourceViewController.view.bounds.height : transitionContext.containerView.bounds.height sourceViewController.view.frame.origin.y = self.presenting ? -sourceViewController.view.bounds.height : transitionContext.containerView.bounds.height