ios 旋转后如何让 UIPopoverController 保持相同的位置?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9065109/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 16:34:46  来源:igfitidea点击:

How to make UIPopoverController keep same position after rotating?

objective-ciosuipopovercontrollerautorotateuipopover

提问by B.S.

I can't keep popover the same position on the screen after rotation. Is there any good way to do that, because just setting some frame to popover works terrible after rotating.popover.frame = CGRectMake(someFrame);After rotation popover looks fine only if it is in the center of the screen.

旋转后我无法在屏幕上保持弹出窗口的相同位置。有什么好的方法可以做到这一点,因为在旋转后仅将一些框架设置为 popover 效果很差。popover.frame = CGRectMake(someFrame);旋转弹出框只有在屏幕中央时才看起来不错。

回答by Max MacLeod

Apple has a Q&A on exactly this issue. You can find the details here:

Apple 就这个问题进行了问答。您可以在此处找到详细信息:

Technical Q&A QA1694 Handling Popover Controllers During Orientation Changes

技术问答 QA1694 在方向更改期间处理弹出控制器

Basically, the technique explains that in your view controller's didRotateFromInterfaceOrientationmethod, you will present the pop over again as follows:

基本上,该技术解释了在您的视图控制器的didRotateFromInterfaceOrientation方法中,您将按如下方式再次显示弹出窗口:

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

For more information, have a read of the article above, and also the UIPopoverController Class Reference:

有关更多信息,请阅读上面的文章,以及UIPopoverController 类参考

If the user rotates the device while a popover is visible, the popover controller hides the popover and then shows it again at the end of the rotation. The popover controller attempts to position the popover appropriately for you but you may have to present it again or hide it altogether in some cases. For example, when displayed from a bar button item, the popover controller automatically adjusts the position (and potentially the size) of the popover to account for changes to the position of the bar button item. However, if you remove the bar button item during the rotation, or if you presented the popover from a target rectangle in a view, the popover controller does not attempt to reposition the popover. In those cases, you must manually hide the popover or present it again from an appropriate new position. You can do this in the didRotateFromInterfaceOrientation: method of the view controller that you used to present the popover.

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

回答by John Stricker

As of iOS 8.0.2 willRotateToInterfaceOrientation will not have any effect. As mhrrt mentioned, you need to use the delegate method:

从 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

So for example if you want your popover to appear directly below a button that was pressed, you would use the following code:

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

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

回答by Markus Rautopuro

In iOS 7 you can use - (void)popoverController:(UIPopoverController *)popoverController willRepositionPopoverToRect:(inout CGRect *)rect inView:(inout UIView *__autoreleasing *)viewto reposition your UIPopoverController's view on interface orientation change.

在 iOS 7 中,您可以使用- (void)popoverController:(UIPopoverController *)popoverController willRepositionPopoverToRect:(inout CGRect *)rect inView:(inout UIView *__autoreleasing *)view在界面方向更改时重新定位 UIPopoverController 的视图。

See the UIPopoverControllerDelegatedocumentation.

请参阅UIPopoverControllerDelegate文档

回答by dead_soldier

You can do this in didRotateFromInterfaceOrientation:method of the view controller that you used to present the popover.

您可以在didRotateFromInterfaceOrientation:用于呈现弹出框的视图控制器的方法中执行此操作。

Use setPopoverContentSize:animated:method for setting the size of the popover.

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

回答by qix

UIPopoverControllerwas deprecated in ios9 in favor of UIPopoverPresentationControllerintroduced in ios8. (I went through this transition also when going from UIActionSheetto UIAlertController.) You have two choices (example in obj-C):

UIPopoverController在 ios9 中不推荐使用,而支持在ios8 中引入的 UIPopoverPresentationController。(从UIActionSheet到时我也经历了这个转换UIAlertController。)你有两个选择(obj-C 中的例子):

A. Implement the UIViewControllermethod below (UIKit calls this method before changing the size of a presented view controller's view).

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. Alternatively, when configuring your UIPopoverPresentationControllerto present, also set its delegate. e.g. your presenting vc can implement UIPopoverPresentationControllerDelegateand assign itself as the delegate. Then implement the delegate method:

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;
}

回答by Zaraki

For 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
}

回答by sabiland

I've tried just to set new rect (rect.initialize(...)) and it works.

我试过只设置新的 rect (rect.initialize(...)) 并且它有效。

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

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

回答by mhrrt

I have similar problem which I resolve by this

我有类似的问题,我由此解决

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

Where myfieldis frame from which you want to show your popover and myscrollviewis container view in which you add your popover as subview(in my case its my scrollview, instead of putting inView:self.viewI use inView:myscrollview).

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

回答by sagar.nikam

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

  1. 初始化 PopOver 控制器

    var popoverContent: PopoverContentViewController?
    
  2. PopOver 控制器的编写定义

    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. 当前弹出控制器

    self.present(popoverContent,动画:true,完成:nil)

  4. 编写下面的方法并为 popover 分配新的大小:

    覆盖 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) }

回答by Rand

I have popoverPresentationController that I present on a view that has a "fake" nav bar. So I can't attach the popoverPresentationController to a barButtonItem. My popup appears in the right place but does not when the screen rotates.

我有 popoverPresentationController,我在一个具有“假”导航栏的视图上显示它。所以我不能将 popoverPresentationController 附加到 barButtonItem。我的弹出窗口出现在正确的位置,但在屏幕旋转时不会出现。

So for some reason popoverPresentationController(_ popoverPresentationController: UIPopoverPresentationController, willRepositionPopoverTo rect: UnsafeMutablePointer<CGRect>, in view: AutoreleasingUnsafeMutablePointer<UIView>)does not get called for me.

所以出于某种原因popoverPresentationController(_ popoverPresentationController: UIPopoverPresentationController, willRepositionPopoverTo rect: UnsafeMutablePointer<CGRect>, in view: AutoreleasingUnsafeMutablePointer<UIView>),我没有被召唤。

To work around this (iOS 12, Swift 4.2) I added constraints to the popup in the completion closure when calling present. Now my popup stays where I would expect it too.

为了解决这个问题(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
                }
            }
        }