From 52c39dbc4d4c7739eb86c0d1f2c2c39228f883b6 Mon Sep 17 00:00:00 2001 From: Riley Testut Date: Mon, 16 Oct 2017 13:03:00 -0700 Subject: [PATCH] Adds ability to search for games from GamesViewController --- ...lerContextTransitioning+Conveniences.swift | 4 +- .../GameCollectionViewController.swift | 18 +++-- .../Game Selection/GamesViewController.swift | 70 ++++++++++++++++++- .../Segues/GamesStoryboardSegue.swift | 68 +++++++++--------- External/Roxas | 2 +- 5 files changed, 120 insertions(+), 42 deletions(-) diff --git a/Delta/Extensions/UIViewControllerContextTransitioning+Conveniences.swift b/Delta/Extensions/UIViewControllerContextTransitioning+Conveniences.swift index 3c7453e..94d9ba4 100644 --- a/Delta/Extensions/UIViewControllerContextTransitioning+Conveniences.swift +++ b/Delta/Extensions/UIViewControllerContextTransitioning+Conveniences.swift @@ -21,11 +21,11 @@ extension UIViewControllerContextTransitioning /// UIViews var sourceView: UIView { - return self.sourceViewController.view + return self.view(forKey: .from) ?? self.sourceViewController.view } var destinationView: UIView { - return self.destinationViewController.view + return self.view(forKey: .to) ?? self.destinationViewController.view } diff --git a/Delta/Game Selection/GameCollectionViewController.swift b/Delta/Game Selection/GameCollectionViewController.swift index b947f39..68f3112 100644 --- a/Delta/Game Selection/GameCollectionViewController.swift +++ b/Delta/Game Selection/GameCollectionViewController.swift @@ -17,9 +17,9 @@ import SDWebImage class GameCollectionViewController: UICollectionViewController { - var gameCollection: GameCollection! { + var gameCollection: GameCollection? { didSet { - self.title = self.gameCollection.shortName + self.title = self.gameCollection?.shortName self.updateDataSource() } } @@ -41,11 +41,12 @@ class GameCollectionViewController: UICollectionViewController } } + internal let dataSource: RSTFetchedResultsCollectionViewPrefetchingDataSource + weak var activeEmulatorCore: EmulatorCore? private var activeSaveState: SaveStateProtocol? - private let dataSource: RSTFetchedResultsCollectionViewPrefetchingDataSource private let prototypeCell = GridCollectionViewCell() private var _performing3DTouchTransition = false @@ -215,7 +216,12 @@ private extension GameCollectionViewController func updateDataSource() { let fetchRequest: NSFetchRequest = Game.fetchRequest() - fetchRequest.predicate = NSPredicate(format: "ANY %K == %@", #keyPath(Game.gameCollections), self.gameCollection) + + if let gameCollection = self.gameCollection + { + fetchRequest.predicate = NSPredicate(format: "ANY %K == %@", #keyPath(Game.gameCollections), gameCollection) + } + fetchRequest.sortDescriptors = [NSSortDescriptor(key: #keyPath(Game.name), ascending: true)] fetchRequest.returnsObjectsAsFaults = false @@ -424,7 +430,7 @@ extension GameCollectionViewController: UIViewControllerPreviewingDelegate { func previewingContext(_ previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? { - guard self.gameCollection.identifier != GameType.unknown.rawValue else { return nil } + guard self.gameCollection?.identifier != GameType.unknown.rawValue else { return nil } guard let collectionView = self.collectionView, @@ -598,7 +604,7 @@ extension GameCollectionViewController { override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { - guard self.gameCollection.identifier != GameType.unknown.rawValue else { return } + guard self.gameCollection?.identifier != GameType.unknown.rawValue else { return } let cell = collectionView.cellForItem(at: indexPath) let game = self.dataSource.item(at: indexPath) diff --git a/Delta/Game Selection/GamesViewController.swift b/Delta/Game Selection/GamesViewController.swift index 260a271..4a969cb 100644 --- a/Delta/Game Selection/GamesViewController.swift +++ b/Delta/Game Selection/GamesViewController.swift @@ -35,12 +35,18 @@ class GamesViewController: UIViewController } } + override var preferredStatusBarStyle: UIStatusBarStyle { + return .lightContent + } + private var pageViewController: UIPageViewController! private var placeholderView: RSTPlaceholderView! private var pageControl: UIPageControl! private let fetchedResultsController: NSFetchedResultsController + private var searchController: RSTSearchController? + override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { fatalError("initWithNibName: not implemented") } @@ -88,6 +94,11 @@ extension GamesViewController self.navigationController?.navigationBar.barStyle = .blackTranslucent self.navigationController?.toolbar.barStyle = .blackTranslucent + if #available(iOS 11.0, *) + { + self.prepareSearchController() + } + self.updateTheme() } @@ -153,6 +164,48 @@ extension GamesViewController /// UI private extension GamesViewController { + @available(iOS 11.0, *) + func prepareSearchController() + { + let searchResultsController = self.storyboard?.instantiateViewController(withIdentifier: "gameCollectionViewController") as! GameCollectionViewController + searchResultsController.gameCollection = nil + searchResultsController.theme = self.theme + searchResultsController.activeEmulatorCore = self.activeEmulatorCore + + let placeholderView = RSTPlaceholderView() + placeholderView.textLabel.text = NSLocalizedString("No Games Found", comment: "") + placeholderView.detailTextLabel.text = NSLocalizedString("Please make sure the name is correct, or try searching for another game.", comment: "") + + switch self.theme + { + case .opaque: searchResultsController.dataSource.placeholderView = placeholderView + case .translucent: + let vibrancyView = UIVisualEffectView(effect: UIVibrancyEffect(blurEffect: UIBlurEffect(style: .dark))) + vibrancyView.contentView.addSubview(placeholderView, pinningEdgesWith: .zero) + searchResultsController.dataSource.placeholderView = vibrancyView + } + + self.searchController = RSTSearchController(searchResultsController: searchResultsController) + self.searchController?.searchableKeyPaths = [#keyPath(Game.name)] + self.searchController?.searchHandler = { [weak searchController, weak searchResultsController] (searchValue, _) in + if searchController?.searchBar.text?.isEmpty == false + { + self.pageViewController.view.isHidden = true + } + else + { + self.pageViewController.view.isHidden = false + } + + searchResultsController?.dataSource.predicate = searchValue.predicate + return nil + } + self.navigationItem.searchController = self.searchController + self.navigationItem.hidesSearchBarWhenScrolling = false + + self.definesPresentationContext = true + } + func updateTheme() { switch self.theme @@ -190,7 +243,7 @@ private extension GamesViewController let indexPath = IndexPath(row: safeIndex, section: 0) let viewController = self.storyboard?.instantiateViewController(withIdentifier: "gameCollectionViewController") as! GameCollectionViewController - viewController.gameCollection = self.fetchedResultsController.object(at: indexPath) as! GameCollection + viewController.gameCollection = self.fetchedResultsController.object(at: indexPath) as? GameCollection viewController.theme = self.theme viewController.activeEmulatorCore = self.activeEmulatorCore @@ -402,6 +455,21 @@ extension GamesViewController: UIPageViewControllerDataSource, UIPageViewControl } } +extension GamesViewController: UISearchResultsUpdating +{ + func updateSearchResults(for searchController: UISearchController) + { + if searchController.searchBar.text?.isEmpty == false + { + self.pageViewController.view.isHidden = true + } + else + { + self.pageViewController.view.isHidden = false + } + } +} + //MARK: - NSFetchedResultsControllerDelegate - /// NSFetchedResultsControllerDelegate extension GamesViewController: NSFetchedResultsControllerDelegate diff --git a/Delta/Game Selection/Segues/GamesStoryboardSegue.swift b/Delta/Game Selection/Segues/GamesStoryboardSegue.swift index c4c74a1..f71a281 100644 --- a/Delta/Game Selection/Segues/GamesStoryboardSegue.swift +++ b/Delta/Game Selection/Segues/GamesStoryboardSegue.swift @@ -85,48 +85,51 @@ extension GamesStoryboardSegue: UIViewControllerAnimatedTransitioning snapshotView.alpha = 1.0 transitionContext.containerView.addSubview(snapshotView) - // Ensures navigation controller toolbar (if visible) has been added to view heirachy, allowing us to add constraints - transitionContext.containerView.layoutIfNeeded() - // We add extra padding around the existing navigation bar and toolbar so they never appear to be detached from the edges of the screen during the overshooting of the spring animation var topPaddingToolbar: UIToolbar? = nil var bottomPaddingToolbar: UIToolbar? = nil - if let navigationController = transitionContext.destinationViewController as? UINavigationController - { - let padding: CGFloat = 44 + // Must be wrapped in no-animation block to prevent iOS 11 search bar from not appearing. + UIView.performWithoutAnimation { + // Ensures navigation controller toolbar (if visible) has been added to view heirachy, allowing us to add constraints + transitionContext.containerView.layoutIfNeeded() - if !navigationController.isNavigationBarHidden + if let navigationController = transitionContext.destinationViewController as? UINavigationController { - let topToolbar = UIToolbar(frame: CGRect.zero) - topToolbar.translatesAutoresizingMaskIntoConstraints = false - topToolbar.barStyle = navigationController.toolbar.barStyle - transitionContext.destinationView.insertSubview(topToolbar, belowSubview: navigationController.navigationBar) + let padding: CGFloat = 44 - topToolbar.bottomAnchor.constraint(equalTo: navigationController.navigationBar.bottomAnchor).isActive = true - topToolbar.centerXAnchor.constraint(equalTo: navigationController.navigationBar.centerXAnchor).isActive = true - topToolbar.widthAnchor.constraint(equalTo: navigationController.navigationBar.widthAnchor, constant: padding * 2).isActive = true - topToolbar.heightAnchor.constraint(equalTo: navigationController.navigationBar.heightAnchor, constant: padding).isActive = true + if !navigationController.isNavigationBarHidden + { + let topToolbar = UIToolbar(frame: CGRect.zero) + topToolbar.translatesAutoresizingMaskIntoConstraints = false + topToolbar.barStyle = navigationController.toolbar.barStyle + transitionContext.destinationView.insertSubview(topToolbar, at: 1) + + topToolbar.topAnchor.constraint(equalTo: navigationController.navigationBar.topAnchor, constant: -padding).isActive = true + topToolbar.bottomAnchor.constraint(equalTo: navigationController.topViewController!.topLayoutGuide.bottomAnchor).isActive = true + topToolbar.leftAnchor.constraint(equalTo: navigationController.navigationBar.leftAnchor, constant: -padding).isActive = true + topToolbar.rightAnchor.constraint(equalTo: navigationController.navigationBar.rightAnchor, constant: padding).isActive = true + + topPaddingToolbar = topToolbar + } - topPaddingToolbar = topToolbar - } - - if !navigationController.isToolbarHidden - { - let bottomToolbar = UIToolbar(frame: CGRect.zero) - bottomToolbar.translatesAutoresizingMaskIntoConstraints = false - bottomToolbar.barStyle = navigationController.toolbar.barStyle - transitionContext.destinationView.insertSubview(bottomToolbar, belowSubview: navigationController.navigationBar) - - bottomToolbar.topAnchor.constraint(equalTo: navigationController.toolbar.topAnchor).isActive = true - bottomToolbar.centerXAnchor.constraint(equalTo: navigationController.toolbar.centerXAnchor).isActive = true - bottomToolbar.widthAnchor.constraint(equalTo: navigationController.toolbar.widthAnchor, constant: padding * 2).isActive = true - bottomToolbar.heightAnchor.constraint(equalTo: navigationController.toolbar.heightAnchor, constant: padding).isActive = true - - bottomPaddingToolbar = bottomToolbar + if !navigationController.isToolbarHidden + { + let bottomToolbar = UIToolbar(frame: CGRect.zero) + bottomToolbar.translatesAutoresizingMaskIntoConstraints = false + bottomToolbar.barStyle = navigationController.toolbar.barStyle + transitionContext.destinationView.insertSubview(bottomToolbar, belowSubview: navigationController.navigationBar) + + bottomToolbar.topAnchor.constraint(equalTo: navigationController.toolbar.topAnchor).isActive = true + bottomToolbar.bottomAnchor.constraint(equalTo: navigationController.toolbar.bottomAnchor, constant: padding).isActive = true + bottomToolbar.leftAnchor.constraint(equalTo: navigationController.toolbar.leftAnchor, constant: -padding).isActive = true + bottomToolbar.rightAnchor.constraint(equalTo: navigationController.toolbar.rightAnchor, constant: padding).isActive = true + + bottomPaddingToolbar = bottomToolbar + } } } - + self.animator.addAnimations { snapshotView.alpha = 0.0 transitionContext.destinationView.transform = CGAffineTransform.identity @@ -163,3 +166,4 @@ extension GamesStoryboardSegue: UIViewControllerAnimatedTransitioning self.animator.startAnimation() } } + diff --git a/External/Roxas b/External/Roxas index 7434aef..192682b 160000 --- a/External/Roxas +++ b/External/Roxas @@ -1 +1 @@ -Subproject commit 7434aef0372aca1d0b12cc4b8a6a37df034aae7c +Subproject commit 192682bb389830944d257946d870e1d50ae4c42b