iOS 8 - UIPopoverPresentationController moving popover

后端 未结 6 1291
遇见更好的自我
遇见更好的自我 2020-12-25 13:40

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

相关标签:
6条回答
  • 2020-12-25 14:13

    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
    
    0 讨论(0)
  • 2020-12-25 14:19

    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.

    0 讨论(0)
  • 2020-12-25 14:20

    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.

    0 讨论(0)
  • 2020-12-25 14:23

    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()
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-25 14:29

    iOS 12.3

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

    0 讨论(0)
  • 2020-12-25 14:33

    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!

    0 讨论(0)
提交回复
热议问题