iOS 8 - UIPopoverPresentationController moving popover

雨燕双飞 提交于 2019-12-18 11:12:22

问题


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.


回答1:


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!




回答2:


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()
        }
    }
}



回答3:


I used the same method as mentioned in another answer by @Rowan_Jones, however I didn't want the popover's size to actually change. Even by fractions of a point. I realized that you can set the preferredContentSize multiple times back to back, but visually it's size will only change to match the last value.

[vc.popoverPresentationController setSourceRect:newSourceRect];
CGSize finalDesiredSize = CGSizeMake(320, 480);
CGSize tempSize = CGSizeMake(finalDesiredSize.width, finalDesiredSize.height + 1);
[vc setPreferredContentSize:tempSize];
[vc setPreferredContentSize:finalDesiredSize];

So even if finalDesiredSize is the same as your initial preferredContentSize this will cause the popover to be updated, even though it's size doesn't actually change.




回答4:


Here is an example for how to recenter the popover:

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

     *rect = CGRectMake((CGRectGetWidth((*view).bounds)-2)*0.5f,(CGRectGetHeight((*view).bounds)-2)*0.5f, 2, 2);

I have also used this method to ensure that the popover moved to the correct location after moving by setting the *rect and the *view to the original sourceRect and sourceView.

As an additional note, I don't believe that this method is called when the popover's source is set using a bar button item.




回答5:


I'm posting this because I don't have enough points to vote or comment. :) @turbs's answer worked for me perfectly. It should be the accepted answer.

Setting *rect to the rect you need in the delegate method:

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



回答6:


iOS 12.3

[vc.popoverPresentationController setSourceRect:newSourceRect]; [vc.popoverPresentationController.containerView setNeedsLayout];



来源:https://stackoverflow.com/questions/24457063/ios-8-uipopoverpresentationcontroller-moving-popover

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!