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

旋转后,我不能在屏幕上保持弹出相同的位置。 有没有什么好方法可以做到这一点,因为只是设置一些框架到弹出窗口旋转后工作很糟糕。 popover.frame = CGRectMake(someFrame); 旋转弹出后,只有当它位于屏幕的中心时才会看起来很好。

苹果就此问题进行了问答。 你可以在这里找到详细信息:

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

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

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

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

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

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

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

请参阅UIPopoverControllerDelegate 文档 。

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

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

我有类似的问题,我解决了这个问题

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

myfield是你想要显示你的myfield的框架, myscrollview是容器视图,你在其中添加你的popover作为子视图(在我的情况下是我的scrollview,而不是放入inView:self.view inView:myscrollview我使用inView:myscrollview )。

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

 func popoverPresentationController(popoverPresentationController: UIPopoverPresentationController, willRepositionPopoverToRect rect: UnsafeMutablePointer, inView view: AutoreleasingUnsafeMutablePointer) { if popoverPresentationController.presentedViewController.view.tag == Globals.PopoverTempTag { rect.initialize(getForPopupSourceRect()) } } 

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

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

 - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator { [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; [coordinator animateAlongsideTransition:nil completion:^(id _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; } 

对于Swift:

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

我遇到了同样的问题。 不是每次都通过跟踪它所呈现的源矩形/视图来执行-presentPopoverFromRect而是将-presentPopoverFromRect子类UIPopoverController 。 完成后,您所要做的就是设置UIBarButtonItem / UIView,从那里显示popover。 您甚至可以选择显示自定义框架中的弹出窗口,该框架可以作为NSString值传入。

CSPopoverController.h

 #import  // The original popover controller would not re-orientate itself when the orientation change occurs. To tackle that issue, this subclass is created @interface CSPopoverController : UIPopoverController @property (nonatomic, strong) NSString *popoverDisplaySourceFrame; // Mutually Exclusive. If you want to set custom rect as source, make sure that popOverDisplaySource is nil @property (nonatomic, strong) id popoverDisplaySource; // Mutually exclusive. If UIBarButtonItem is set to it, popoverDisplaySourceFrame is neglected. @property (nonatomic, strong) UIView *popoverDisplayView; @property (nonatomic, assign, getter = shouldAutomaticallyReorientate) BOOL automaticallyReorientate; -(void)reorientatePopover; @end 

CSPopoverController.m

 #import "CSPopoverController.h" @implementation CSPopoverController @synthesize popoverDisplaySourceFrame = popoverDisplaySourceFrame_; -(NSString*)popoverDisplaySourceFrame { if (nil==popoverDisplaySourceFrame_) { if (nil!=self.popoverDisplaySource) { if ([self.popoverDisplaySource isKindOfClass:[UIView class]]) { UIView *viewSource = (UIView*)self.popoverDisplaySource; [self setPopoverDisplaySourceFrame:NSStringFromCGRect(viewSource.frame)]; } } } return popoverDisplaySourceFrame_; } -(void)setPopoverDisplaySourceFrame:(NSString *)inPopoverDisplaySourceFrame { if (inPopoverDisplaySourceFrame!=popoverDisplaySourceFrame_) { popoverDisplaySourceFrame_ = inPopoverDisplaySourceFrame; [self reorientatePopover]; } } @synthesize popoverDisplaySource = popoverDisplaySource_; -(void)setPopoverDisplaySource:(id)inPopoverDisplaySource { if (inPopoverDisplaySource!=popoverDisplaySource_) { [self unlistenForFrameChangeInView:popoverDisplaySource_]; popoverDisplaySource_ = inPopoverDisplaySource; [self reorientatePopover]; if ([popoverDisplaySource_ isKindOfClass:[UIView class]]) { UIView *viewSource = (UIView*)popoverDisplaySource_; [self setPopoverDisplaySourceFrame:NSStringFromCGRect(viewSource.frame)]; } if (self.shouldAutomaticallyReorientate) { [self listenForFrameChangeInView:popoverDisplaySource_]; } } } @synthesize popoverDisplayView = popoverDisplayView_; -(void)setPopoverDisplayView:(UIView *)inPopoverDisplayView { if (inPopoverDisplayView!=popoverDisplayView_) { popoverDisplayView_ = inPopoverDisplayView; [self reorientatePopover]; } } @synthesize automaticallyReorientate = automaticallyReorientate_; -(void)setAutomaticallyReorientate:(BOOL)inAutomaticallyReorientate { if (inAutomaticallyReorientate!=automaticallyReorientate_) { automaticallyReorientate_ = inAutomaticallyReorientate; if (automaticallyReorientate_) { [self listenForAutorotation]; [self listenForFrameChangeInView:self.popoverDisplaySource]; } else { [self unlistenForAutorotation]; [self unlistenForFrameChangeInView:self.popoverDisplaySource]; } } } -(void)listenForAutorotation { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object:nil]; } -(void)unlistenForAutorotation { [[NSNotificationCenter defaultCenter] removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil]; } -(void)listenForFrameChangeInView:(id)inView { // Let's listen for changes in the view's frame and adjust the popover even if the frame is updated if ([inView isKindOfClass:[UIView class]]) { UIView *viewToObserve = (UIView*)inView; [viewToObserve addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew context:nil]; } } -(void)unlistenForFrameChangeInView:(id)inView { if ([inView isKindOfClass:[UIView class]]) { UIView *viewToObserve = (UIView*)inView; [viewToObserve removeObserver:self forKeyPath:@"frame"]; } } // TODO: Dealloc is not called, check why? !!! - (void)dealloc { [self unlistenForFrameChangeInView:self.popoverDisplaySource]; [self unlistenForAutorotation]; DEBUGLog(@"dealloc called for CSPopoverController %@", self); } #pragma mark - Designated initializers -(id)initWithContentViewController:(UIViewController *)viewController { self = [super initWithContentViewController:viewController]; if (self) { [self popoverCommonInitializations]; } return self; } -(void)popoverCommonInitializations { [self setAutomaticallyReorientate:YES]; } #pragma mark - Frame -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if (object==self.popoverDisplaySource) { [self setPopoverDisplaySourceFrame:nil]; [self reorientatePopover]; } } #pragma mark - Orientation -(void)orientationChanged:(NSNotification *)inNotification { [self reorientatePopover]; } -(void)reorientatePopover { [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(performReorientatePopover) object:nil]; // if ([self isPopoverVisible]) { [self performSelector:@selector(performReorientatePopover) withObject:nil afterDelay:0.0]; } } -(void)performReorientatePopover { if (self.popoverDisplaySourceFrame && self.popoverDisplayView) { [self presentPopoverFromRect:CGRectFromString(self.popoverDisplaySourceFrame) inView:self.popoverDisplayView permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES]; } else if (self.popoverDisplaySource && [self.popoverDisplaySource isKindOfClass:[UIBarButtonItem class]]) { UIBarButtonItem *barButton = (UIBarButtonItem*)self.popoverDisplaySource; [self presentPopoverFromBarButtonItem:barButton permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES]; } } @end 

用法:

如果它是您呈现它的UIBarButtonItem:

 CSPopoverController *popOverCont = [[CSPopoverController alloc]initWithContentViewController:navCont]; self.popOver = popOverCont; [popOverCont setPopoverDisplaySource:self.settingsButtonItem]; 

如果它是一个UIView你从哪里呈现popover:

 CSPopoverController *popOver = [[CSPopoverController alloc] initWithContentViewController:navigation]; self.iPadPopoverController = popOver; [newDateVC setIPadPopoverController:self.iPadPopoverController]; [popOver setPopoverDisplaySource:inButton]; [popOver setPopoverDisplayView:inView]; 

对于iOS> 8,John Strickers的答案有所帮助,但没有按照我的意愿去做。

这是适合我的解决方案。 (如果你想下载一个完整的示例项目,请点击此处: https : //github.com/appteur/uipopoverExample )

我创建了一个属性来保存我想要呈现的任何popover,并且还添加了一个属性来跟踪sourceRect,另一个属性用于按钮的视图我希望弹出箭头指向。

 @property (nonatomic, weak) UIView *activePopoverBtn; @property (nonatomic, strong) PopoverViewController *popoverVC; @property (nonatomic, assign) CGRect sourceRect; 

触发弹出窗口的按钮位于UIToolbar中。 点击它时会运行以下创建和启动弹出窗口的方法。

 -(void) buttonAction:(id)sender event:(UIEvent*)event { NSLog(@"ButtonAction"); // when the button is tapped we want to display a popover, so setup all the variables needed and present it here // get a reference to which button's view was tapped (this is to get // the frame to update the arrow to later on rotation) // since UIBarButtonItems don't have a 'frame' property I found this way is easy UIView *buttonView = [[event.allTouches anyObject] view]; // set our tracker properties for when the orientation changes (handled in the viewWillTransitionToSize method above) self.activePopoverBtn = buttonView; self.sourceRect = buttonView.frame; // get our size, make it adapt based on our view bounds CGSize viewSize = self.view.bounds.size; CGSize contentSize = CGSizeMake(viewSize.width, viewSize.height - 100.0); // set our popover view controller property self.popoverVC = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:@"PopoverVC"]; // configure using a convenience method (if you have multiple popovers this makes it faster with less code) [self setupPopover:self.popoverVC withSourceView:buttonView.superview // this will be the toolbar sourceRect:self.sourceRect contentSize:contentSize]; [self presentViewController:self.popoverVC animated:YES completion:nil]; } 

‘setupPopover:withSourceView:sourceRect:contentSize方法只是一个方便的方法来设置popoverPresentationController属性,如果你打算显示多个弹出窗口并希望它们配置相同。 它的实现如下。

 // convenience method in case you want to display multiple popovers -(void) setupPopover:(UIViewController*)popover withSourceView:(UIView*)sourceView sourceRect:(CGRect)sourceRect contentSize:(CGSize)contentSize { NSLog(@"\npopoverPresentationController: %@\n", popover.popoverPresentationController); popover.modalPresentationStyle = UIModalPresentationPopover; popover.popoverPresentationController.delegate = self; popover.popoverPresentationController.sourceView = sourceView; popover.popoverPresentationController.sourceRect = sourceRect; popover.preferredContentSize = contentSize; popover.popoverPresentationController.permittedArrowDirections = UIPopoverArrowDirectionDown; popover.popoverPresentationController.backgroundColor = [UIColor whiteColor]; } 

对于iOS 8及更高版本,当设备旋转时,viewWillTransitionToSize:withTransitionCoordinator将在视图控制器上调用。

我在我的视图控制器类中实现了这个方法,如下所示。

 // called when rotating a device - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator { NSLog(@"viewWillTransitionToSize [%@]", NSStringFromCGSize(size)); // resizes popover to new size and arrow location on orientation change [coordinator animateAlongsideTransition:^(id _Nonnull context) { if (self.popoverVC) { // get the new frame of our button (this is our new source rect) CGRect viewframe = self.activePopoverBtn ? self.activePopoverBtn.frame : CGRectZero; // update our popover view controller's sourceRect so the arrow will be pointed in the right place self.popoverVC.popoverPresentationController.sourceRect = viewframe; // update the preferred content size if we want to adapt the size of the popover to fit the new bounds self.popoverVC.preferredContentSize = CGSizeMake(self.view.bounds.size.width -20, self.view.bounds.size.height - 100); } } completion:^(id _Nonnull context) { // anything you want to do when the transition completes }]; } 

斯威夫特3:

  class MyClass: UIViewController, UIPopoverPresentationControllerDelegate { ... var popover:UIPopoverPresentationController? ... // Where you want to set the popover... popover = YourViewController?.popoverPresentationController popover?.sourceRect = CGRect(x: self.view.bounds.midX, y: self.view.bounds.midY, width: 0, height: 0) popover?.delegate = self ... // override didRotate... override func didRotate(from fromInterfaceOrientation: UIInterfaceOrientation) { popover?.sourceRect = CGRect(x: self.view.bounds.midX, y: self.view.bounds.midY, width: 0, height: 0) } }