После поворота я не могу оставить позади ту же позицию на экране. Есть ли хороший способ сделать это, потому что просто установка какого-то кадра на popover работает ужасно после поворота. popover.frame = CGRectMake(someFrame);
После поворота popover выглядит отлично, только если он находится в центре экрана.Как заставить 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:
Если пользователь поворачивает устройство, в то время как popover является vi sible, контроллер pop скрывает popover, а затем показывает его снова в конце вращения . Контроллер popover пытается позиционировать popover для вас, но вам, возможно, придется представить его снова или скрыть его вообще в некоторых случаях. Например, при отображении на панели пункта элемент управления popover автоматически настраивает позицию (и, возможно, размер) popover для учета изменений на позиции элемента кнопки панели. Однако, если вы удалили элемент кнопки во время вращения, или если вы представили popover от целевого прямоугольника в представлении, контроллер popover не будет пытаться выполнить , чтобы переместить popover. В этих случаях вы должны вручную скрыть popover или представить его снова из соответствующей новой позиции. Вы можете сделать это в методе didRotateFromInterfaceOrientation: метод представления контроллера , который вы использовали для представления popover.
Вы можете сделать это в didRotateFromInterfaceOrientation:
методе контроллера вида, который вы использовали для представления popover.
Используйте метод setPopoverContentSize:animated:
для установки размера popover.
Этот метод изменяет происхождение popover? Мне не нужно изменять размер содержимого popover, просто чтобы сохранить происхождение. –
У меня аналогичная проблема, которую я разрешить этого
[myPop presentPopoverFromRect:myfield.frame inView:myscrollview permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
Где myfield
это кадр из которого вы хотите показать свой поповер и myscrollview
вид контейнера, в котором вы добавите поповер, как подвид (в моем случае его мой scrollview, вместо того, чтобы положить inView:self.view
Я использую inView:myscrollview
).
У меня была такая же проблема. Вместо того, чтобы выполнять -presentPopoverFromRect
каждый раз, отслеживая исходный прямоугольник/представление, из которого оно представлено, я подклассифицировал UIPopoverController
. После этого все, что вам нужно сделать, это установить UIBarButtonItem/UIView, из которого должно отображаться popover. Вы даже можете выбрать отображение popover из настраиваемого фрейма, который может быть передан как значение NSString.
CSPopoverController.ч:
#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];
В прошивке 7 вы можете использовать - (void)popoverController:(UIPopoverController *)popoverController willRepositionPopoverToRect:(inout CGRect *)rect inView:(inout UIView *__autoreleasing *)view
, чтобы изменить вид вашего UIPopoverController на изменении ориентации интерфейса.
См. UIPopoverControllerDelegate
documentation.
Спасибо, это сработало для меня на iOS 8 – almas
От 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 Ответ Джона Стрикерса помог, но не сделал того, что я хотел этого сделать.
Вот решение, которое сработало для меня. (Если вы хотите загрузить полный образец проекта, он находится здесь: https://github.com/appteur/uipopoverExample)
Я создал свойство держать любое popover, которое я хотел представить, а также добавил свойство отслеживать sourceRect и другое для просмотра кнопки, которую я хотел стрелка для перемещения по стрелке.
@property (nonatomic, weak) UIView *activePopoverBtn;
@property (nonatomic, strong) PopoverViewController *popoverVC;
@property (nonatomic, assign) CGRect sourceRect;
Кнопка, запускающая мой popover, находится в UIToolbar. При нажатии на него выполняется следующий метод, который создает и запускает popover.
-(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, если вы планируете отображать несколько Popovers и хотите, чтобы они настроены так же. Это реализация ниже.
// 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];
}
Для прошивки 8 и вверх по viewWillTransitionToSize: withTransitionCoordinator Получить называется на контроллере представления, когда устройство вращается.
Я реализовал этот метод в классе представления класса представления, как показано ниже.
// 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 (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):
А. Реализация метода 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;
}
Swift 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
Для 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
}
Просто проверьте эту ссылку также .. http://stackoverflow.com/ Вопросы/3670981/adjust-uipopovercontroller-position-after-resize –
Спасибо за: presentPopoverFromRect: inView можно использовать, когда отображается popover –