IOS8 how to move an active popover
Asked Answered
L

4

8

I have developed an app for iOS7 and now trying to update it for iOS8. Issue i have is the following:

The app screen orientation can be rotated and a few buttons in some cases move drastically. I have a few popovers that point to these buttons, so if a popover is open when screen rotates, button moves, i need the popover to also.

In iOS7 i did this by the following: When screen rotated i updated the constraints

- (void) updateViewConstraints 
{
    if (UIInterfaceOrientationIsLandscape(self.interfaceOrientation))
    {
        self.Button.constant = (CGFloat)10;
    }
    else
    {
        self.Button.constant = (CGFloat)5;
    }
    [super updateViewConstraints];
} 

I also move the popover

- (void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation{

    if(TempDisplayPopoverController == examplePopoverController)
    {
        [examplePopoverController presentPopoverFromRect:[self ExamplePopoverPosition] inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
    }
}

I initially load the popover

- (void) LoadPopover{
    examplePopover = [[examplep alloc] initWithNibName:@"exampleP" bundle:nil];
    [examplePopover setDelegate:self];
    examplePopoverController = [[UIPopoverController alloc] initWithContentViewController: examplePopover];
    [examplePopoverController setDelegate:self];

    examplePopoverController.popoverContentSize = examplePopover.view.frame.size;

    TempDisplayPopoverController = examplePopoverController;


    if ([examplePopoverController isPopoverVisible])
    {
        [examplePopoverController dismissPopoverAnimated:YES];
    }
    else
    {
        [examplePopoverController presentPopoverFromRect:[self ExamplePopoverPosition] inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
    }
}

[self ExamplePopoverPosition] just returns button position.

This all worked fine, i was happy, iPad was happy and all behaved.

Now due to iOS8 i have to change a few bits.

self.interfaceOrientation is depreciated

[examplePopoverController presentPopoverFromRect:[self ExamplePopoverPosition] inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];

in didRotateFromInterfaceOrientation throws an error

"Application tried to represent an active popover presentation: <UIPopoverPresentationController: 0x7bf59280>"

I've managed to rectify self.interfaceOrientation by

- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    [self SetUpScreen:toInterfaceOrientation];
}

- (void) SetUpScreen:(UIInterfaceOrientation)toInterfaceOrientation{
    if (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft ||
        toInterfaceOrientation == UIInterfaceOrientationLandscapeRight)
    {
        self.Button.constant = (CGFloat)10;
    }
    else
    {
        self.Button.constant = (CGFloat)5;
    }
    [super updateViewConstraints];
}

but have no idea how to resolve the popover issue. I have tried

popoverController: willRepositionPopoverToRect: inView:

but just can't to seem to get it to work.

Can anyone advice

Thanks

Lubra answered 4/10, 2014 at 8:6 Comment(4)
same problem here. We must ask Apple...Degust
hi there, i still haven't solved this< (granted been bit distracted with something else though)Lubra
no nothing yet thats stable unfortunatelyLubra
access the contents of your xib file as a UIView, and update the frame in updateViewConstraints https://mcmap.net/q/23718/-how-to-load-a-xib-file-in-a-uiviewBraasch
S
4

In iOS 8 you can use -viewWillTransitionToSize:withTransitionCoordinator: to handle screen size (and orientation) changes:

- (void)viewWillTransitionToSize:(CGSize)size
       withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator 
{
    [_popover dismissPopoverAnimated:NO];
    [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
         // Update your layout for the new size, if necessary.  
         // Compare size.width and size.height to see if you're in landscape or portrait.
    } completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
        [_popover presentPopoverFromRect:[self popoverFrame] 
                                  inView:self.view
                permittedArrowDirections:UIPopoverArrowDirectionAny 
                                animated:NO];
    }];
}

When you implement this method, the deprecated rotation methods like willAnimateRotationToInterfaceOrientation: will not be called when running on iOS 8.

Sew answered 15/11, 2014 at 19:39 Comment(0)
R
1

When using popoverController:willRepositionPopoverToRect:inView:, when reassigning to the rect parameter, try using:

*rect = myNewRect;

and not:

rect = &presentingRect;

Also, make sure you have properly assigned the popover controller's delegate.

Reconnoitre answered 15/11, 2014 at 16:50 Comment(0)
G
0

First, you don't need to dismiss and present the popover on rotation. UIPopoverPresentationController does that for you. You don't even need to update sourceView/sourceRect once they are set on creating the popover.

Now, the trick with animate(alongsideTransition: ((UIViewControllerTransitionCoordinatorContext) -> Void)?, completion: ((UIViewControllerTransitionCoordinatorContext) -> Void)? = nil) is that you should update your constraints in alongsideTransition closure, not in completion. This way you ensure that UIPopoverPresentationController has the updated sourceRect when restoring the popover at the end of rotation.

What might seem counter-intuitive is that inside alongsideTransition closure you already have your new layout that you derive your constraints calculation from.

Here's an example in Swift:

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)
    coordinator.animate(alongsideTransition: { _ in
        if self.popover != nil {
            // optionally scroll to popover source rect, if inside scroll view
            let rect = ...
            self.scrollView.scrollRectToVisible(rect, animated: false)

            // update source rect constraints
            myConstraint.constant = ...
            myConstrainedView.setNeedsLayout()
            myConstrainedView.layoutIfNeeded()
        }
    }, completion: nil)
}
Golden answered 15/10, 2016 at 22:20 Comment(0)
L
-1

Very interesting - I got this to work without updating the position manually. I don't know why this works though.

let buttonContainer = UIView(frame: CGRectMake(0, 0, 44, 44))
let button = UIButton(frame: CGRectMake(0, 0, 44, 44))
buttonContainer.addSubview(button)
view.addSubview(buttonContainer)

popover!.presentPopoverFromRect(button, inView: button.superview!, permittedArrowDirections: .Any, animated: true)

Put the button that the popover is presenting from inside a "container view". Then the popover will automatically adjust location upon orientation change.

Livery answered 19/4, 2015 at 15:24 Comment(1)
Please try this solution out before down voting. I know this sounds crazy, but it works for me.Livery

© 2022 - 2024 — McMap. All rights reserved.