2012-01-30 15 views
19

Non riesco a mantenere il popover nella stessa posizione sullo schermo dopo la rotazione. C'è un buon modo per farlo, perché l'impostazione di alcuni frame su popover funziona terribilmente dopo la rotazione. popover.frame = CGRectMake(someFrame); Dopo la rotazione, il popover funziona bene solo se si trova al centro dello schermo.Come rendere UIPopoverController mantenere la stessa posizione dopo la rotazione?

+0

controllare Proprio questo link anche .. http://stackoverflow.com/ domande/3670981/adjust-uipopovercontroller-position-after-resize –

+0

Grazie per: presentPopoverFromRect: inView può essere utilizzato quando popover è visibile –

risposta

28

Apple ha un Q & A su esattamente questo problema. È possibile trovare i dettagli qui:

Technical Q&A QA1694 Handling Popover Controllers During Orientation Changes

In sostanza, la tecnica spiega che nel metodo didRotateFromInterfaceOrientation del controller della vista, si presenterà la comparsa di nuovo nel modo seguente:

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

Per ulteriori informazioni, leggere l'articolo sopra, e anche il UIPopoverController Class Reference:

Se l'utente ruota il dispositivo mentre un popover è vi Sible, il controller pop-up nasconde il popover e lo mostra di nuovo alla fine della rotazione di . Il controller popover tenta di posizionare il popover in modo appropriato per te ma potrebbe essere necessario presentarlo di nuovo o nasconderlo in tutti i casi in alcuni casi. Ad esempio, quando viene visualizzato da un elemento della barra , il controller popover regola automaticamente la posizione (e potenzialmente la dimensione) del popover per tenere conto delle modifiche apportate a la posizione dell'elemento del pulsante della barra. Tuttavia, se si rimuove la voce di pulsante durante la rotazione o se il popover è stato presentato da a un rettangolo di destinazione in una vista, il controller di popover non tenta di riposizionare il popover . In questi casi, è necessario nascondere manualmente il pop-up o ripresentarlo da una nuova posizione appropriata. È possibile eseguire nel metodo didRotateFromInterfaceOrientation: del comando di visualizzazione utilizzato per presentare il popover.

5

È possibile farlo nel metodo didRotateFromInterfaceOrientation: del controller di visualizzazione utilizzato per presentare il popover.

Utilizzare il metodo setPopoverContentSize:animated: per impostare la dimensione del popover.

+4

Questo metodo modifica l'origine del popover? Non ho bisogno di cambiare la dimensione del contenuto popover, solo per mantenere l'origine. –

1

Ho problema simile che risolvo da questa

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

Dove myfield è fotogramma da cui si desidera mostrare il vostro popover e myscrollview è vista contenitore in cui si aggiunge il popover come visualizzazione secondaria (nel mio caso la sua la mia scrollview, invece di mettere inView:self.view io uso inView:myscrollview).

0

Ho avuto lo stesso problema. Invece di eseguire -presentPopoverFromRect ogni volta tenendo traccia del rettangolo/vista di origine da cui è presentato, ho creato una sottoclasse di UIPopoverController. Dopo averlo fatto, tutto ciò che devi fare è impostare UIBarButtonItem/UIView da dove deve essere visualizzato il popover. Puoi anche scegliere di visualizzare il popover dal frame personalizzato che può essere passato come valore 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 

Usage:

Se si tratta di un UIBarButtonItem da dove si sta presentando è:

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

Se si tratta di un UIView da dove si sta presentando il popover:

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

In iOS 7 è possibile utilizzare - (void)popoverController:(UIPopoverController *)popoverController willRepositionPopoverToRect:(inout CGRect *)rect inView:(inout UIView *__autoreleasing *)view per riposizionare vista del UIPopoverController sul cambiamento dell'orientamento interfaccia.

Vedere UIPopoverControllerDelegatedocumentation.

+0

Grazie, questo ha funzionato per me su iOS 8 – almas

15

A partire da iOS 8.0.2 willRotateToInterfaceOrientation non avrà alcun effetto. Come mhrrt accennato, è necessario utilizzare il metodo delegato:

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

Così, per esempio, se volete che il vostro popover appaia direttamente sotto un pulsante che è stato premuto, si può usare il seguente codice:

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

Per iOS> 8 la risposta di John Strickers ha aiutato ma non ha fatto ciò che volevo.

Ecco la soluzione che ha funzionato per me. (Se vuoi scaricare un progetto completo di esempio è qui: https://github.com/appteur/uipopoverExample)

Ho creato una proprietà per contenere qualsiasi popover che volevo presentare e ho aggiunto anche una proprietà per tenere traccia di sourceRect e un'altra per la visualizzazione del pulsante che volevo la freccia del popover a cui puntare.

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

Il pulsante che ha attivato il mio popover è in una UIToolbar. Quando viene toccato, esegue il seguente metodo che crea e avvia il 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]; 

} 

Il 'setupPopover: withSourceView: sourceRect: metodo contentSize è semplicemente un metodo comodo per impostare le proprietà popoverPresentationController se si prevede di visualizzare più popovers e vuole che la stessa configurazione. La sua implementazione è sotto

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

Per iOS 8 e il viewWillTransitionToSize: withTransitionCoordinator get chiama il controller della vista durante la rotazione del dispositivo.

Ho implementato questo metodo nella mia classe di controller di visualizzazione come mostrato di seguito.

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

Ho cercato solo di fissare nuovi rect (rect.initialize (...)) e funziona.

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

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

UIPopoverController è stata sconsigliata a ios9 a favore di UIPopoverPresentationController introdotto in iOS 8. (Sono andato attraverso questa transizione anche quando si va UIActionSheet-UIAlertController.) Hai due scelte (esempio in obj-C):

A. Implementare il metodo UIViewController sotto (UIKit chiama questo metodo prima di cambiare la dimensione di una presentati guarda la vista del controller).

- (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. In alternativa, durante la configurazione UIPopoverPresentationController a oggi, impostare anche il suo delegato. per esempio. la tua presentazione vc può implementare UIPopoverPresentationControllerDelegate e assegnarsi come delegato. Quindi implementare il metodo delegato:

- (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' è [deprecato] (https://developer.apple.com/documentation/uikit/uiviewcontroller/1621492-didrotate). Dovresti utilizzare uno dei due approcci che ho menzionato in https://stackoverflow.com/a/41561021/954643, ad es. 'popoverPresentationController (_: willRepositionPopoverTo: in:)' ([docs] (https://developer.apple.com/documentation/uikit/uipopoverpresentationcontrollerdelegate/1622326-popoverpresentationcontroller?preferredLanguage=swift)) Questo è in parte perché è possibile modificare il layout di uno schermo di più di rotazioni ora, ad esempio attraverso le funzionalità multitasking a schermo diviso in ios9 + – qix

0

per 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 
} 
Problemi correlati