如何使UIPopoverController在旋转后保持相同的位置?
旋转后,屏幕上的相同位置无法保持弹出状态。有没有什么好的方法可以做到这一点,因为在旋转之后,设置一些框架以实现弹出效果非常可怕。 popover.frame = CGRectMake(someFrame);
旋转弹出窗口只有在屏幕中心时才会显示正常。如何使UIPopoverController在旋转后保持相同的位置?
在这个问题上,Apple有一个Q & A.你可以在这里找到细节:
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 Class Reference:
如果用户在弹出窗口是vi时旋转设备sible,弹出窗口 控制器隐藏弹出窗口,然后在 轮换结束时再次显示它。弹出窗口控制器尝试为您适当地定位弹出窗口 ,但在某些情况下,您可能需要再次显示或将其隐藏 。例如,当从条形图按钮项目 中显示时,弹出窗口控制器会自动调整弹出窗口的位置 (以及可能的大小),以考虑对条形按钮项目的位置进行更改( )。但是,如果在旋转过程中删除条形图按钮项 ,或者在视图中显示目标矩形的弹出框 ,则弹窗控制器不会尝试 重新定位弹出窗口。在这些情况下,您必须手动隐藏弹出窗口或将其从适当的新位置再次显示。您可以使用 在didRotateFromInterfaceOrientation中执行此操作:您用于呈现弹出窗口的视图 控制器。
您可以在didRotateFromInterfaceOrientation:
中使用您用来呈现弹出窗口的视图控制器的方法。
使用setPopoverContentSize:animated:
设置弹出窗口大小的方法。
这个方法是否改变了popover的起源?我不需要改变popover内容的大小,只是为了保持原点。 – 2012-01-30 14:32:30
我有我这个
[myPop presentPopoverFromRect:myfield.frame inView:myscrollview permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
哪里myfield
是要展示你的酥料饼和myscrollview
框架解决类似的问题,是在你把你的酥料饼作为子视图容器视图(在我的情况下,其我的scrollview,而不是把inView:self.view
我用inView:myscrollview
)。
我有同样的问题。我不是每次通过跟踪从中呈现它的源矩形/视图来执行-presentPopoverFromRect
,而是将其分类为UIPopoverController
。做完之后,你所要做的就是设置UIBarButtonItem/UIView来自弹出窗口的显示位置。您甚至可以选择显示自定义框架中的弹出窗口,该框架可以作为NSString值传入。
CSPopoverController。^ h:
#import <UIKit/UIKit.h>
// 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:
CSPopoverController *popOver = [[CSPopoverController alloc] initWithContentViewController:navigation];
self.iPadPopoverController = popOver;
[newDateVC setIPadPopoverController:self.iPadPopoverController];
[popOver setPopoverDisplaySource:inButton];
[popOver setPopoverDisplayView:inView];
在iOS中7,您可以使用- (void)popoverController:(UIPopoverController *)popoverController willRepositionPopoverToRect:(inout CGRect *)rect inView:(inout UIView *__autoreleasing *)view
重新定位你的UIPopoverController对接口的方向变化视图。
查看UIPopoverControllerDelegate
documentation。
谢谢,这适用于我在iOS 8 – almas 2015-03-02 19:29:10
从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> 8约翰Strickers答案有帮助,但没有做我想做的事情。
这是为我工作的解决方案。 (如果你想下载一个完整的示例项目,它在这里:https://github.com/appteur/uipopoverExample)
我创建了一个属性来容纳我想呈现的任何弹出窗口,并且还添加了一个属性来跟踪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方法是一个简单的便捷方法,如果你打算显示多个popovers和希望它们配置的相同设置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 GET就是所谓的视图时控制器设备旋转上。
我在呈现视图控制器类中实现了此方法,如下所示。
// called when rotating a device
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
NSLog(@"viewWillTransitionToSize [%@]", NSStringFromCGSize(size));
// resizes popover to new size and arrow location on orientation change
[coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> _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<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
// anything you want to do when the transition completes
}];
}
我试过只是设置新的矩形(rect.initialize(...)),它的工作原理。
func popoverPresentationController(popoverPresentationController: UIPopoverPresentationController, willRepositionPopoverToRect rect: UnsafeMutablePointer<CGRect>, inView view: AutoreleasingUnsafeMutablePointer<UIView?>) {
if popoverPresentationController.presentedViewController.view.tag == Globals.PopoverTempTag
{
rect.initialize(getForPopupSourceRect())
}
}
UIPopoverController
被弃用ios9赞成UIPopoverPresentationController在iOS8上推出。 (我通过这个转变从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;
}
斯威夫特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)
}
}
'didRotate' [已弃用](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621492-didrotate)。您应该使用我在https://stackoverflow.com/a/41561021/954643中提到的两种方法之一,例如'popoverPresentationController(_:willRepositionPopoverTo:in:)'([docs](https://developer.apple.com/documentation/uikit/uipopoverpresentationcontrollerdelegate/1622326-popoverpresentationcontroller?preferredLanguage=swift))这部分是因为您可以更改现在屏幕的布局不止是旋转,比如通过ios9 +中的分屏多任务功能 – qix 2017-06-07 17:24:35
对于斯威夫特:
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
}
只是检查这个环节也.. http://stackoverflow.com/ questions/3670981/adjust-uipopovercontroller-position-after-resize – 2012-01-30 14:24:25
Thanks for:presentPopoverFromRect:inView可用于popover可见时 – 2012-01-30 15:56:10