Question

In my app I have 3 UIPopOvers. They appear when user taps buttons in the toolbar. I need to make the popovers appear in the correct place when user rotates iPad if the popover is already opened (like the -willAnimateRotationToInterfaceOrientation:).

How can I do it?

Thanks in advance!

Was it helpful?

Solution 2

The only solution I've found so far is just closing the popover when the device is rotated/

OTHER TIPS

In iOS 7.0 and later, it can be done by implementing following method available in UIPopoverControllerDelegate:

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

For popovers that were presented using the presentPopoverFromRect method, the popover controller calls this method when the interface orientation changes.

Here is a code fragment from one of my projects. Basically, if the popover is showing, you present the popover again in the method didRotateFromInterfaceOrientation:, which is sent to the view controller after the user interface rotation has taken place. (The willRotate... and willAnimateRotation... methods are called before the rotation has taken place, so it is the wrong place for the presentPopover... method call.)

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
  // if the popover is showing, adjust its position after the re-orientation by presenting it again:
  if (self.myPopoverController != nil)  // if the popover is showing (replace with your own test if you wish)
  {
    [self.myPopoverController presentPopoverFromRect:attachmentRect
                                              inView:myView
                            permittedArrowDirections:UIPopoverArrowDirectionUp
                                            animated:YES];
  }    
}

In the above, self.myPopoverController is a property of my view controller where I store a reference to the popover when it is created. When I dismiss and discard the popover under normal circumstances, I take care to set this property to nil, so I can check it for 'non-nil'ness to decide whether or not the popover is being shown.

Note, however, that you don't need to dismiss the popover before the rotation takes place. Just present the same popover again. (This is where keeping a reference to the popover comes in handy.)

In your case, where the popover emanates from a toolbar button, you would use something like the following instead:

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
  // if the popover is showing, adjust its position after the re-orientation by presenting it again:
  if (self.myPopoverController != nil)  // if the popover is showing (replace with your own test if you wish)
  {
    [self.myPopoverController presentPopoverFromBarButtonItem:barButtonItem
                                     permittedArrowDirections:UIPopoverArrowDirectionAny
                                                     animated:YES];
  }    
}

If you simply use the presentPopoverFromBarButtonItem method to present your popover then the popover will automatically move to the correct position for the new button position when the device is rotated.

Are you calling presentPopoverFromBarButtonItem or FromRect? Are you making any changes to the BarButtonItem on rotate?

Apple's documentation specifically states you need to manage position on rotation for FromRect or if you modify the bar button item. See fourth paragraph in http://developer.apple.com/library/ios/#documentation/uikit/reference/UIPopoverController_class/Reference/Reference.html

I've run into this same issue a couple of times. I typically just make a method to show the popover centered like this:

- (void) showPopoverForSize:(CGSize) size center:(CGPoint) center {
   CGFloat width = size.width;
    CGFloat height = size.height;
    CGFloat x = center.x - width / 2;
    CGFloat y = center.y - height / 2;
    CGRect frame = CGRectMake(x, y, width, height);
    popover.popoverContentSize = frame.size;

    [popover presentPopoverFromRect:frame inView:self.view permittedArrowDirections:0 animated:YES];
}

Then on didRotate I do:

- (void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
    [super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
    if (popover.isPopoverVisible)
        [self showPopoverForSize:popover.popoverContentSize center:self.view.center];
}

This will put the popover in the center for any orientation.

At the beginning of the change of orientation dismiss the popover, and after the change of orientation is completed it again and it changes present its position on the screen:

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
            [_popover dismissPopoverAnimated:YES];
        }
    }

    - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
    {
        if (_popover) {
            [_popover presentPopoverFromRect:frameRect
                                                      inView:self.view
                                    permittedArrowDirections:UIPopoverArrowDirectionUp
                                                    animated:YES];
        }    
    }

Performing popover function:

func presentPopover() {
    self.popoverFlag = true
    //Presenting PopOver code goes here
    // ...
}

Dismissing presented popover on changing orientation:

override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {

        if self.isKindOfClass(ViewController) && self.popoverFlag{
            guard self.presentedViewController != nil else { return }
            dispatch_async(dispatch_get_main_queue()) {
                self.presentedViewController!.dismissViewControllerAnimated(true, completion: nil)
            }
        }
    }

Presenting popover again:

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

You should use UIPopoverPresentationControllerDelegate method:

func popoverPresentationController(_ popoverPresentationController: UIPopoverPresentationController, willRepositionPopoverTo rect: UnsafeMutablePointer<CGRect>, in view: AutoreleasingUnsafeMutablePointer<UIView>)

and update the rect value.

See @Hugo Alonso's answer here

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top