2012-01-30 12 views
19

No puedo mantener Popover en la misma posición en la pantalla después de la rotación. ¿Hay alguna buena manera de hacerlo, porque simplemente establecer un marco para popover funciona terrible después de la rotación. popover.frame = CGRectMake(someFrame); Después del popover de rotación se ve bien solo si está en el centro de la pantalla.¿Cómo hacer que UIPopoverController mantenga la misma posición después de rotar?

+0

simplemente si este enlace también .. http://stackoverflow.com/ preguntas/3670981/adjust-uipopovercontroller-position-after-resize –

+0

Gracias por: presentPopoverFromRect: inView se puede usar cuando popover es visible –

Respuesta

28

Apple tiene un Q & A sobre exactamente este problema. Puede encontrar los detalles aquí:

Technical Q&A QA1694 Handling Popover Controllers During Orientation Changes

Básicamente, la técnica explica que en el método de su controlador de vista didRotateFromInterfaceOrientation, se le presentará el estallido de nuevo de la siguiente manera:

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

Para obtener más información, tener una lectura del artículo anterior, y también el UIPopoverController Class Reference:

Si el usuario gira el dispositivo mientras que un popover es vi sible, el controlador popover oculta la popover y luego muestra de nuevo al final de la rotación . El controlador popover intentos para colocar el popover apropiada para usted, pero puede que tenga que presentar de nuevo o esconderlo por completo en algunos casos. Por ejemplo, cuando se visualiza desde un elemento de botón de barra , el controlador popover ajusta automáticamente la posición (y posiblemente el tamaño) del popover para tener en cuenta los cambios a la posición del elemento del botón de barra. Sin embargo, si elimina la barra elemento de botón durante la rotación, o si presentó el popover de un rectángulo de destino en una vista, el controlador popover no intenta para reposicionar el popover. En esos casos, debe ocultar manualmente el popover o presentarlo nuevamente desde una nueva posición apropiada. Puede hacer esto en el didRotateFromInterfaceOrientation: método de la vista del controlador de que utilizó para presentar el popover.

5

Puede hacerlo en el método didRotateFromInterfaceOrientation: del controlador de vista que utilizó para presentar el popover.

Uso setPopoverContentSize:animated: método para establecer el tamaño de la popover.

+4

¿Este método cambia el origen de popover? No necesito cambiar el tamaño del contenido de popover, solo para mantener el origen. –

1

que tienen un problema similar que resuelvo por esta

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

Dónde myfield es el marco desde el que desea mostrar su popover y myscrollview es vista recipiente en el que se agrega el popover como subvista (en mi caso su mi scrollview, en lugar de poner inView:self.view utilizo inView:myscrollview).

0

Tuve el mismo problema. En lugar de realizar -presentPopoverFromRect cada vez siguiendo el rectángulo/vista de origen desde el que se presenta, he subclasificado UIPopoverController. Después de hacerlo, todo lo que tiene que hacer es configurar UIBarButtonItem/UIView desde donde debe mostrarse el popover. Incluso puede optar por mostrar el popover desde el marco personalizado que se puede pasar como un valor de 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 

Uso:

Si se trata de un UIBarButtonItem desde donde se está presentando es:

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

Si se trata de un UIView desde donde se está presentando el popover:

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

En iOS 7 se puede utilizar para volver a colocar - (void)popoverController:(UIPopoverController *)popoverController willRepositionPopoverToRect:(inout CGRect *)rect inView:(inout UIView *__autoreleasing *)view vista de su UIPopoverController sobre el cambio de orientación de la interfaz.

Ver el UIPopoverControllerDelegatedocumentation.

+0

Gracias, esto funcionó para mí en iOS 8 – almas

15

A partir de iOS 8.0.2 willRotateToInterfaceOrientation no tendrá ningún efecto. Como mhrrt mencionado, es necesario utilizar el método delegado:

- (void)popoverController:(UIPopoverController *)popoverController willRepositionPopoverToRect:(inout CGRect *)rect inView:(inout UIView *__autoreleasing *)view

Así por ejemplo, si usted quiere que su popover que aparezca directamente debajo de un botón que se ha pulsado, se utiliza el siguiente código:

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

Para iOS> 8 La respuesta de John Strickers ayudó pero no hizo lo que yo quería que hiciera.

Aquí está la solución que funcionó para mí. (Si desea descargar un proyecto de muestra completo aquí: https://github.com/appteur/uipopoverExample)

Creé una propiedad para contener cualquier popover que quisiera presentar y también agregué una propiedad para rastrear el sourceRect y otra para la vista del botón que quería la flecha de popover para apuntar.

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

El botón que activó mi popover está en una barra de UIToolbar. Cuando se toca se ejecuta el siguiente método que crea e inicia el 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]; 

} 

El 'setupPopover: withSourceView: sourceRect: Método contentSize es simplemente un método de conveniencia para establecer las propiedades popoverPresentationController si va a mostrar varios panecillos y quiere que configuran la misma. Su implementación está abajo.

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

Para iOS 8 y hasta el viewWillTransitionToSize: withTransitionCoordinator get se llama en el controlador de vista cuando se gira el dispositivo.

Implementé este método en mi clase de controlador de vista presentadora como se muestra a continuación.

// 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

He intentado simplemente establecer un nuevo rect (rect.initialize (...)) y funciona.

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

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

UIPopoverController está desfasada y en ios9 a favor de UIPopoverPresentationController introducido en ios8. (Fui a través de esta transición también cuando se va UIActionSheet-UIAlertController.) Usted tiene dos opciones (ejemplo de obj-C):

A. implementar el método UIViewController a continuación (UIKit llama a este método antes de cambiar el tamaño de una presentados ver la vista del controlador).

- (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. Alternativamente, cuando la configuración de su UIPopoverPresentationController hasta la actualidad, también se establezca su delegado. p.ej. su presentación vc puede implementar UIPopoverPresentationControllerDelegate y asignarse a sí mismo como el delegado. A continuación, poner en práctica el método delegado:

- (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' está [en desuso] (https://developer.apple.com/documentation/uikit/uiviewcontroller/1621492-didrotate). Debería utilizar uno de los dos enfoques que mencioné en https://stackoverflow.com/a/41561021/954643, p. Ej. 'popoverPresentationController (_: willRepositionPopoverTo: in:)' ([docs] (https://developer.apple.com/documentation/uikit/uipopoverpresentationcontrollerdelegate/1622326-popoverpresentationcontroller?preferredLanguage=swift)) Esto se debe en parte a que puede cambiar el diseño de una pantalla por más de rotaciones ahora, como a través de las funciones de multitarea de pantalla dividida en ios9 + – qix

0

para 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 
} 
Cuestiones relacionadas