如何使UIPopoverController在旋转后保持相同的位置?

21

在旋转屏幕后,我无法使弹出窗口保持相同的位置。有没有好的方法来做到这一点,因为仅仅设置一些frame对于弹出窗口在旋转后的效果很差。popover.frame = CGRectMake(someFrame);在屏幕中心的位置弹出窗口看起来还不错。


请也查看这个链接:https://dev59.com/DVDTa4cB1Zd3GeqPIVhh - dead_soldier
感谢使用 presentPopoverFromRect:inView 方法,该方法可用于在气泡窗口可见时调用。 - B.S.
13个回答

30

苹果公司就此问题进行了问答,您可以在此处找到详细信息:

Technical Q&A QA1694 Handling Popover Controllers During Orientation Changes

基本上,该技术解释了在您的视图控制器的didRotateFromInterfaceOrientation方法中,您将再次呈现弹出窗口的方法:

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
    [aPopover presentPopoverFromRect:targetRect.frame inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}

想了解更多信息,请阅读上面的文章以及UIPopoverController类参考

如果用户在弹出窗口可见时旋转设备,则弹出窗口控制器会隐藏弹出窗口,然后在旋转结束时再次显示它。弹出窗口控制器尝试为您适当地定位弹出窗口,但在某些情况下,您可能需要重新呈现它或完全隐藏它。例如,当从一个栏按钮项中显示时,弹出窗口控制器会自动调整弹出窗口的位置(以及可能的大小),以考虑栏按钮项位置的更改。但是,如果您在旋转期间删除了栏按钮项,或者如果您从视图中的目标矩形呈现了弹出窗口,则弹出窗口控制器不会尝试重新定位弹出窗口。在这些情况下,您必须手动隐藏弹出窗口或从一个适当的新位置再次呈现它。您可以在用于呈现弹出窗口的视图控制器的didRotateFromInterfaceOrientation:方法中执行此操作。


2
UIPopoverController已被弃用。您需要使用UIPopoverPresentationController。https://developer.apple.com/documentation/uikit/uipopoverpresentationcontroller - fred

17

iOS 8.0.2版本之后,willRotateToInterfaceOrientation将不再起作用。正如mhrrt所提到的,您需要使用委托方法:

- (void)popoverController:(UIPopoverController *)popoverController willRepositionPopoverToRect:(inout CGRect *)rect inView:(inout UIView *__autoreleasing *)view

例如,如果您希望您的弹出窗口直接出现在按下的按钮下方,则可以使用以下代码:

- (void)popoverController:(UIPopoverController *)popoverController willRepositionPopoverToRect:(inout CGRect *)rect inView:(inout UIView *__autoreleasing *)view
{
   CGRect rectInView = [self.theButton convertRect:self.theButton.frame toView:self.view];
   *rect = CGRectMake(CGRectGetMidX(rectInView), CGRectGetMaxY(rectInView), 1, 1);
   *view = self.view;
}

6
在iOS 7中,你可以使用- (void)popoverController:(UIPopoverController *)popoverController willRepositionPopoverToRect:(inout CGRect *)rect inView:(inout UIView *__autoreleasing *)view方法来重新定位你的UIPopoverController视图,在界面方向改变时。
请查看UIPopoverControllerDelegate 文档

5

您可以在显示弹出窗口的视图控制器的didRotateFromInterfaceOrientation:方法中完成此操作。

使用setPopoverContentSize:animated:方法设置弹出窗口的大小。


4
这种方法会改变弹出视图的起点吗?我不需要改变弹出视图内容的大小,只想保持起点不变。 - B.S.

4

针对 Swift:

func popoverPresentationController(_ popoverPresentationController: UIPopoverPresentationController, willRepositionPopoverTo rect: UnsafeMutablePointer<CGRect>, in view: AutoreleasingUnsafeMutablePointer<UIView>)
{
    rect.pointee = CGRect(x: self.view.frame.size.width, y: 0, width: 1, height: 1) // Set new rect here
}

4

UIPopoverController在iOS9中被弃用,推荐使用iOS8中引入的UIPopoverPresentationController。(我也曾经从UIActionSheet过渡到UIAlertController。)你有两个选择(下面是obj-C示例):

A. 实现下面的UIViewController方法(UIKit在改变呈现视图控制器视图大小之前调用此方法)。

- (void)viewWillTransitionToSize:(CGSize)size
           withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
        [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
        [coordinator animateAlongsideTransition:nil
                                     completion:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
                                         // Fix up popover placement if necessary, *after* the transition.
                                         // Be careful here if a subclass also overrides this method.
                                         if (self.presentedViewController) {
                                             UIPopoverPresentationController *presentationController =
                                                     [self.presentedViewController popoverPresentationController];
                                             UIView *selectedView = /** YOUR VIEW */;
                                             presentationController.sourceView = selectedView.superview;
                                             presentationController.sourceRect = selectedView.frame;
                                         }
                                     }];
    }

B. 或者,当配置你的UIPopoverPresentationController进行演示时,还可以设置它的代理。例如,您的展示VC可以实现UIPopoverPresentationControllerDelegate并将自己分配为代理。然后实现代理方法:

- (void)popoverPresentationController:(UIPopoverPresentationController *)popoverPresentationController
          willRepositionPopoverToRect:(inout CGRect *)rect
                               inView:(inout UIView * _Nonnull *)view {
    UIView *selectedView = /** YOUR VIEW */;
    // Update where the arrow pops out of in the view you selected.
    *view = selectedView;
    *rect = selectedView.bounds;
}

谢谢您!在尝试了许多次后,选项B对我来说完美无缺。 - Zach

2

我尝试过仅设置新矩形(rect.initialize(...)),它可以工作。

func popoverPresentationController(popoverPresentationController: UIPopoverPresentationController, willRepositionPopoverToRect rect: UnsafeMutablePointer<CGRect>, inView view: AutoreleasingUnsafeMutablePointer<UIView?>) {

        if popoverPresentationController.presentedViewController.view.tag == Globals.PopoverTempTag
        {
            rect.initialize(getForPopupSourceRect())
        }
    }

1

我有类似的问题,我通过这种方式解决了它。

[myPop presentPopoverFromRect:myfield.frame inView:myscrollview permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];

其中myfield是您想要显示弹出窗口的框架,而myscrollview是您将弹出窗口作为子视图添加到其中的容器视图(在我的情况下,它是我的滚动视图,而不是将inView:self.view放入其中,我使用inView:myscrollview)。


1
  1. Initialize PopOver Controller

    var popoverContent: PopoverContentViewController?
    
  2. Write Defination for PopOver Controller

    popoverContent = self.storyboard?.instantiateViewController(withIdentifier: "PopoverContentViewController") as? PopoverContentViewController
    popoverContent?.modalPresentationStyle = .popover
    let popover = popoverContent?.popoverPresentationController!
    popover?.delegate = self
    popoverContent?.preQuestionInfoPopUpViewDelegateObject = self
    popover?.permittedArrowDirections = UIPopoverArrowDirection()
    popover?.sourceView = self.view
    popover?.sourceRect = CGRect(x: self.view.bounds.midX, y: self.view.bounds.midY, width: 330, height: 330)
    
  3. Present PopOver Controller

    self.present(popoverContent, animated: true, completion:nil)

  4. Write below method and assign new size to popover:

    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { let popover = popoverContent?.popoverPresentationController! popover?.sourceRect = CGRect(x: size.width/2, y: size.height/2, width: 0, height: 0) }


0

我有一个popoverPresentationController,我在一个带有“假”导航栏的视图上呈现它。因此,我无法将popoverPresentationController附加到barButtonItem上。我的弹出窗口出现在正确的位置,但在屏幕旋转时不会出现。

因此,由于某种原因,popoverPresentationController(_ popoverPresentationController:UIPopoverPresentationController,willRepositionPopoverTo rect:UnsafeMutablePointer<CGRect>,in view:AutoreleasingUnsafeMutablePointer<UIView>)没有为我调用。

为了解决这个问题(iOS 12,Swift 4.2),我在调用present时添加了弹出窗口完成闭包中的约束。现在我的弹出窗口停留在我期望的位置。

                present(viewController, animated: true) { [weak self] in
            DDLogDebug(String(describing: viewController.view.frame))
            if let containerView = viewController.popoverPresentationController?.containerView,
            let presentedView = viewController.popoverPresentationController?.presentedView,
            let imageView = self?.headerView.settingsButton {
                withExtendedLifetime(self) {
                    let deltaY:CGFloat = presentedView.frame.origin.y - imageView.frame.maxY
                    let topConstraint = NSLayoutConstraint.init(item: presentedView, attribute: .top, relatedBy: .equal, toItem: imageView.imageView, attribute: .bottom, multiplier: 1, constant: deltaY)
                    topConstraint?.priority = UILayoutPriority(rawValue: 999)
                    topConstraint?.isActive = true
                    let heightContraint = NSLayoutConstraint.init(item: presentedView, attribute: .height, relatedBy: .equal, toItem: containerView, attribute: .height, multiplier: 0.75, constant: -deltaY)
                    heightContraint?.isActive = true
                    let leftConstraint = NSLayoutConstraint.init(item: presentedView, attribute: .left, relatedBy: .equal, toItem: containerView, attribute: .left, multiplier: 1, constant: presentedView.frame.origin.x)
                    leftConstraint.isActive = true
                    let widthConstraint = NSLayoutConstraint.init(item: presentedView, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: presentedView.frame.width)
                    widthConstraint.isActive = true
                    presentedView.translatesAutoresizingMaskIntoConstraints = false
                }
            }
        }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接