Search code examples
iosstoryboard

Combine Peek and Pop with Popover in iOS


Is it possible to combine a peek and pop with a popover in iOS?

I want to use peek and pop with iPhones that support 3D Touch and at the same time a popover on iPads.

When I try to combine it in storyboard I get the error "Couldn't compile connection".

enter image description here


Solution

  • I have found the answer by myself.

    The problem is, that the anchor point of the PopOver points to the prototype cell, which is dynamically created, so the system is not sure which cell is the anchor.

    So the solution to this is the following:

    1. Set the anchor point of the pop over to the table view
    2. Remove the peek & pop functionality from the storyboard segue as we need to do this in code.
    3. Go to your UITableViewDataSource (which in my case is the view controller) and add the following to your cellForRowAt() using the cell as source view:
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "myCellIdentifier", for: indexPath) as! MyTableViewCell
        let item = items[indexPath.row]
        // Pass the item here...
        registerForPreviewing(with: self, sourceView: cell) // <== Add this
        return cell
    }
    
    1. After that you have to conform to the protocol UIViewControllerPreviewingDelegate like this:
    extension ListeningVC: UIViewControllerPreviewingDelegate {
        func previewingContext(_ previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {
            guard let indexPath = tableView.indexPathForRow(at: location) else {
                return nil
            }
            // get the item you want to pass using the index path
            let item = items[indexPath.row]
            // get the destination view controller
            let destination = UIStoryboard(name: "...", bundle: nil).instantiateInitialViewController()
            // Pass the item here...
            return destination // return the destination view controller
        }
    
        func previewingContext(_ previewingContext: UIViewControllerPreviewing, commit viewControllerToCommit: UIViewController) {
            // either present the view controller or add it to your navigation controller here
            present(viewControllerToCommit, animated: true)
        }
    }
    
    1. If you want, you can center the anchor point (source view) a bit:
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        switch segue.identifier {
        case "mySegueIdentifier":
            guard let cell = sender as? MyTableViewCell else { return }
            guard let indexPath = tableView.indexPath(for: cell) else { return }
            let item = items[indexPath.row]
            let destination = segue.destination
            // Pass the item here...
            if let popOver = segue.destination.popoverPresentationController {
                // set the cell as source view
                let origin = CGPoint(x: 100, y: cell.frame.origin.y)
                let size = CGSize(width: 200, height: cell.frame.height)
                popOver.sourceView = tableView
                popOver.sourceRect = CGRect(origin: origin, size: size)
            }
        default:
            break
        }
    }
    
    1. Be happy that this works and give an upvote to this answer. :)