2012-01-30 5 views
19

После поворота я не могу оставить позади ту же позицию на экране. Есть ли хороший способ сделать это, потому что просто установка какого-то кадра на popover работает ужасно после поворота. popover.frame = CGRectMake(someFrame); После поворота popover выглядит отлично, только если он находится в центре экрана.Как заставить UIPopoverController сохранить такое же положение после вращения?

+0

Просто проверьте эту ссылку также .. http://stackoverflow.com/ Вопросы/3670981/adjust-uipopovercontroller-position-after-resize –

+0

Спасибо за: presentPopoverFromRect: inView можно использовать, когда отображается popover –

ответ

28

У 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.

5

Вы можете сделать это в didRotateFromInterfaceOrientation: методе контроллера вида, который вы использовали для представления popover.

Используйте метод setPopoverContentSize:animated: для установки размера popover.

+4

Этот метод изменяет происхождение popover? Мне не нужно изменять размер содержимого popover, просто чтобы сохранить происхождение. –

1

У меня аналогичная проблема, которую я разрешить этого

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

Где myfield это кадр из которого вы хотите показать свой поповер и myscrollview вид контейнера, в котором вы добавите поповер, как подвид (в моем случае его мой scrollview, вместо того, чтобы положить inView:self.view Я использую inView:myscrollview).

0

У меня была такая же проблема. Вместо того, чтобы выполнять -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]; 
6

В прошивке 7 вы можете использовать - (void)popoverController:(UIPopoverController *)popoverController willRepositionPopoverToRect:(inout CGRect *)rect inView:(inout UIView *__autoreleasing *)view, чтобы изменить вид вашего UIPopoverController на изменении ориентации интерфейса.

См. UIPopoverControllerDelegatedocumentation.

+0

Спасибо, это сработало для меня на iOS 8 – almas

15

От 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; 
} 
0

Для 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 
    }]; 
} 
1

Я попытался просто установить новый 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()) 
     } 
    } 
0

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

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

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

0

Для 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 
} 

 Смежные вопросы

  • Нет связанных вопросов^_^