Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS 8 - UIPopoverPresentationController moving popover

Tags:

I am looking for an effective way to re-position a popover using the new uipopoverpresentationcontroller. I have succesfully presented the popover, and now I want to move it without dismissing and presenting again. I am having trouble using the function:

(void)popoverPresentationController:(UIPopoverPresentationController *)popoverPresentationController
      willRepositionPopoverToRect:(inout CGRect *)rect
                           inView:(inout UIView **)view

I know it's early in the game, but it anyone has an example of how to do this efficiently I would be grateful if you shared it with me. Thanks in advance.

like image 602
angerboy Avatar asked Jun 27 '14 16:06

angerboy


2 Answers

Unfortunately this hacky workaround is the only solution I've found:

[vc.popoverPresentationController setSourceRect:newSourceRect];
[vc setPreferredContentSize:CGRectInset(vc.view.frame, -0.01, 0.0).size];

This temporarily changes the content size of the presented view, causing the popover and arrow to be repositioned. The temporary change in size is not visible.

It seems this is a problem Apple need to fix - changing the sourceView or sourceRect properties of UIPopoverPresentationController does nothing when it's already presenting a popover (without this workaround).

Hope this works for you too!

like image 87
Rowan Jones Avatar answered Sep 19 '22 15:09

Rowan Jones


I had luck using containerView?.setNeedsLayout() and containerView?.layoutIfNeeded() after changing the sourceRect of the popoverPresentationController, like so:

func movePopoverTo(_ newRect: CGRect) {
    let popover = self.presentedViewController as? MyPopoverViewController {
        popover.popoverPresentationController?.sourceRect = newRect
        popover.popoverPresentationController?.containerView?.setNeedsLayout()
        popover.popoverPresentationController?.containerView?.layoutIfNeeded()
    }
}

And even to have a popover follow a tableView cell without having to change anything:

class MyTableViewController: UITableViewController {

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "MyPopoverSegue" {
            guard let controller = segue.destination as? MyPopoverViewController else { fatalError("Expected destination controller to be a 'MyPopoverViewController'!") }
            guard let popoverPresentationController = controller.popoverPresentationController else { fatalError("No popoverPresentationController!") }
            guard let rowIndexPath = sender as? IndexPath else { fatalError("Expected sender to be an 'IndexPath'!") }
            guard myData.count > rowIndexPath.row else { fatalError("Index (\(rowIndexPath.row)) Out Of Bounds for array (count: \(myData.count))!") }
            if self.presentedViewController is MyPopoverViewController {
                self.presentedViewController?.dismiss(animated: false)
            }
            popoverPresentationController.sourceView = self.tableView
            popoverPresentationController.sourceRect = self.tableView.rectForRow(at: rowIndexPath)
            popoverPresentationController.passthroughViews = [self.tableView]
            controller.configure(myData[rowIndexPath.row])
        }
        super.prepare(for: segue, sender: sender)
    }
}

// MARK: - UIScrollViewDelegate

extension MyTableViewController {
    override func scrollViewDidScroll(_ scrollView: UIScrollView) {
        if let popover = self.presentedViewController as? MyPopoverViewController {
            popover.popoverPresentationController?.containerView?.setNeedsLayout()
            popover.popoverPresentationController?.containerView?.layoutIfNeeded()
        }
    }
}
like image 26
chown Avatar answered Sep 19 '22 15:09

chown