1

I have a UICollectionView that shows 'Peek' when 3D Touch'ed. As default behavior, 'Peek' ignores navigation bars.

However, I do want to show a bar just as in iMessage Peek shown below:

enter image description here

Both Collection View Controller & Peek View Controller are inside Navigation View Controller.

I have following snippet from Apple's Sample code below that I am trying to modify in to above needs:

extension ChatTableViewController: UIViewControllerPreviewingDelegate {
    func previewingContext(_ previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {
        guard let indexPath = tableView.indexPathForRow(at: location) else { return nil }

        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        let viewController = storyboard.instantiateViewController(withIdentifier: ChatDetailViewController.identifier)
        guard let chatDetailViewController = viewController as? ChatDetailViewController else { return nil }

        chatDetailViewController.chatItem = chatItem(at: indexPath)
        let cellRect = tableView.rectForRow(at: indexPath)
        previewingContext.sourceRect = previewingContext.sourceView.convert(cellRect, from: tableView)
        chatDetailViewController.isReplyButtonHidden = true

        return chatDetailViewController
    }

    func previewingContext(_ previewingContext: UIViewControllerPreviewing, commit viewControllerToCommit: UIViewController) {
        if let chatDetailViewController = viewControllerToCommit as? ChatDetailViewController {
            chatDetailViewController.isReplyButtonHidden = false
        }
        show(viewControllerToCommit, sender: self)
    }

UPDATE

Thanks to Leo Natan I was able to accomplish above:

extension ChatTableViewController: UIViewControllerPreviewingDelegate {
    func previewingContext(_ previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {
        guard let indexPath = tableView.indexPathForRow(at: location) else { return nil }

        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        let viewController = storyboard.instantiateViewController(withIdentifier: ChatDetailViewController.identifier)
        guard let chatDetailViewController = viewController as? ChatDetailViewController else { return nil }

        chatDetailViewController.chatItem = chatItem(at: indexPath)
        let cellRect = tableView.rectForRow(at: indexPath)
        previewingContext.sourceRect = previewingContext.sourceView.convert(cellRect, from: tableView)
        chatDetailViewController.isReplyButtonHidden = true

        let navigationController = UINavigationController(rootViewController: viewController
        return navigationController
    }

    func previewingContext(_ previewingContext: UIViewControllerPreviewing, commit viewControllerToCommit: UIViewController) {
        if let chatDetailViewController = viewControllerToCommit as? ChatDetailViewController {
            chatDetailViewController.isReplyButtonHidden = false
        }
        show(viewControllerToCommit, sender: self)
    }

However, this creates a NEW Navigation Controller. If I want to end up with the same same Navigation Controller, I can do this:

func previewingContext(_ previewingContext: UIViewControllerPreviewing, commit viewControllerToCommit: UIViewController) {
    if let chatDetailViewController = viewControllerToCommit as? ChatDetailViewController {
        chatDetailViewController.isReplyButtonHidden = false
    }

    show((viewControllerToCommit as UIController).viewControllers[0], sender: self)
}

show((viewControllerToCommit as UIController).viewControllers[0], sender: self) extracts the viewController out of it's Navigation Controller. Does this have any downsides?

Gizmodo
  • 3,151
  • 7
  • 45
  • 92

1 Answers1

1

In previewingContext(_:, viewControllerForLocation:), wrap your view controller in a UINavigationController and return that.

Léo Natan
  • 56,823
  • 9
  • 150
  • 195
  • self.navigationController!.pushViewController(chatDetailViewController, animated: false) I tried that, causing a crash. – Gizmodo Jan 21 '17 at 20:55
  • 1
    Why are you pushing? Return a *new* navigation controller, wrapping `chatDetailViewController`. – Léo Natan Jan 21 '17 at 20:55
  • Perfect. let navigationController = 'UINavigationController(rootViewController: viewController)' and then 'return navigationController'. That worked! – Gizmodo Jan 21 '17 at 21:15
  • I marked the your reply as correct because you answered what I originally asked for. However, by wrapping viewController around a NEW UINavigationController, when the viewController is committed, it's a new navigration controller. It's missing back button and other elements. To fix this, is there a way to return the same UINavigationController the view controller is currently in? – Gizmodo Jan 21 '17 at 21:40
  • 1
    No. You need to throw the wrapper navigation controller and commit the char detail controller directly. – Léo Natan Jan 21 '17 at 21:41