setContentViewController method in popover iOS8 causes app crash
Asked Answered
U

9

12

The setContentViewController method in UIPopoverController seems to be causing an app crash in iOS 8. Just wondering if anybody else also faced this issue in iOS 8. This works without any issue in iOS 7.

The error pointed in the exception seems to be misleading as it states that the setContentViewController should be called after presenting the popover

- (void)buttonPressed {

UIViewController *tableViewController = [UIViewController new];

if(_popover == nil){

    _popover = [[UIPopoverController alloc] initWithContentViewController:tableViewController];

    [_popover presentPopoverFromRect:CGRectMake(self.textField.frame.size.width / 2, self.textField.frame.size.height / 1, 1, 1) inView:self.textField permittedArrowDirections:UIPopoverArrowDirectionLeft animated:YES];
}else{
    [_popover setContentViewController:tableViewController];
}    

}

Here is Stack trace from the crash,

2014-09-11 16:48:39.904 iOS 8 Rotation[3969:67869] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UIPopoverController setContentViewController:animated:] can only be called after the popover has been presented.'
*** First throw call stack:
(
    0   CoreFoundation                      0x01c79df6 __exceptionPreprocess + 182
    1   libobjc.A.dylib                     0x01903a97 objc_exception_throw + 44
    2   CoreFoundation                      0x01c79d1d +[NSException raise:format:] + 141
    3   UIKit                               0x00b1946f -[UIPopoverPresentationController _setContentViewController:animated:] + 89
    4   UIKit                               0x009bb1b4 -[UIPopoverController setContentViewController:animated:] + 155
    5   UIKit                               0x009bb114 -[UIPopoverController setContentViewController:] + 48
    6   iOS 8 Rotation                      0x00046ca5 -[MianViewController buttonPressed] + 933
    7   libobjc.A.dylib                     0x019197cd -[NSObject performSelector:withObject:withObject:] + 84
    8   UIKit                               0x002ef79d -[UIApplication sendAction:to:from:forEvent:] + 99
    9   UIKit                               0x002ef72f -[UIApplication sendAction:toTarget:fromSender:forEvent:] + 64
    10  UIKit                               0x00422a16 -[UIControl sendAction:to:forEvent:] + 69
    11  UIKit                               0x00422e33 -[UIControl _sendActionsForEvents:withEvent:] + 598
    12  UIKit                               0x0042209d -[UIControl touchesEnded:withEvent:] + 660
    13  UIKit                               0x0033faba -[UIWindow _sendTouchesForEvent:] + 874
    14  UIKit                               0x00340595 -[UIWindow sendEvent:] + 791
    15  UIKit                               0x00305aa9 -[UIApplication sendEvent:] + 242
    16  UIKit                               0x003158de _UIApplicationHandleEventFromQueueEvent + 20690
    17  UIKit                               0x002ea079 _UIApplicationHandleEventQueue + 2206
    18  CoreFoundation                      0x01b9d7bf __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 15
    19  CoreFoundation                      0x01b932cd __CFRunLoopDoSources0 + 253
    20  CoreFoundation                      0x01b92828 __CFRunLoopRun + 952
    21  CoreFoundation                      0x01b921ab CFRunLoopRunSpecific + 443
    22  CoreFoundation                      0x01b91fdb CFRunLoopRunInMode + 123
    23  GraphicsServices                    0x040cc24f GSEventRunModal + 192
    24  GraphicsServices                    0x040cc08c GSEventRun + 104
    25  UIKit                               0x002ede16 UIApplicationMain + 1526
    26  iOS 8 Rotation                      0x0004774d main + 141
    27  libdyld.dylib                       0x0224cac9 start + 1
    28  ???                                 0x00000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
Uneven answered 11/9, 2014 at 11:50 Comment(4)
Always call an initializer! UIViewController *tableViewController = [UIViewController alloc]; is wrong, it must be [[UIViewController alloc] init] or [[UIViewController alloc] initWithNibName:@"SomeNibName" bundle:nil]. But since a generic, empty view controller is useless you want to instantiate a custom view controller subclass instead.Lepidote
Agreed, changed to [UIViewController new]Uneven
same here. i am not re-creating the popover everytime i have to present it, so i just present it. but it causes random crashes. i really am in a fix... what to doHeinrik
I have the same problem with xcode 6.Twenty
C
4

I just encountered this same error testing our app under iOS 8. In our case, I took the error message at face value and changed a pattern we had in a few places.

The pattern we had to change was: (1) in our view controller's init, instantiate a popover controller instance. (2) on some event, set the popover controller's contentViewController property to the desired vc. (3) call presentPopoverFromRect on the popover controller

and we simply changed step (2) to re-instantiate the popover controller with the desired content vc as an init parameter and ceased setting the contentViewController property (as we're always doing it prior to presenting the popover).

Commorant answered 18/9, 2014 at 19:0 Comment(0)
S
3

I was experiencing this same issue and finally solved it.

I have two buttons, each one shows its own popover. I was reusing the same UIPopoverController to show both of them. The first click worked fine, but then if you clicked the other one the app crashed.

The way I solved it is create a new UIPopoverController on each click:

importImagePickerControlPopoverController=[[UIPopoverController alloc] initWithContentViewController:pickerController];
[importImagePickerControlPopoverController setDelegate:self];

switch(pickerType)
{
    case UIImagePickerControllerSourceTypePhotoLibrary:
    case UIImagePickerControllerSourceTypeSavedPhotosAlbum:
        [importImagePickerControlPopoverController setPopoverContentSize:CGSizeMake(320, 300) animated:YES];
        break;
    case UIImagePickerControllerSourceTypeCamera:
        [importImagePickerControlPopoverController setPopoverContentSize:CGSizeMake(640, 640) animated:YES];
        break;
}
[importImagePickerControlPopoverController setContentViewController:pickerController];
Springwood answered 25/9, 2014 at 17:45 Comment(0)
M
2

Similar to @user3495742 solution:

-(void)testAndSetPopoverWithVc:(UIViewController*)vc {
    if (popover4Menu) {
        if ([popover4Menu isPopoverVisible]) {
            [popover4Menu setContentViewController:vc animated:YES];
            return;
        } else {
            popover4Menu=nil;
        }
    };
    popover4Menu=[[UIPopoverController alloc] initWithContentViewController:vc];
    popover4Menu.delegate=self;
}

Problem rises when reassigning a view controller to an existing popover. Free and realloc popover before assigning new content view controller: this fixed for me.

Mylor answered 8/10, 2014 at 15:51 Comment(3)
Ciao Giorgio, this line popover4Menu=nil will result in a memory leak / dangling pointer. you can achieve the same effect by nilling out the synthesized property which will set the retain count to zero and correctly deallocate the popover.Knockwurst
Thanks @johnpope, I voted this hint up. In my implementation popover4Menu is a class private variable, not a property, so I think assigning it to nil let ARC to manage deallocation correclty, but I've not cheked it in deep yet.Mylor
You can see by subclassing the popover - then override the dealloc method (without calling super dealloc) and set a break point there. It should become clear when you nil out the variable - it won't correctly dealloc. Setting a self.property = nil will correctly release the instance.Knockwurst
T
0

I had the same issue and this is what fixed it for me.

Try This

- (void)buttonPressed{

  UIViewController *tableViewController = [UIViewController new];

  //Check if the popover is nil or not visible
  if(_popover == nil || _popover.popoverVisible == false){

    _popover = [[UIPopoverController alloc] initWithContentViewController:tableViewController];

    [_popover presentPopoverFromRect:CGRectMake(self.textField.frame.size.width / 2, self.textField.frame.size.height / 1, 1, 1) inView:self.textField permittedArrowDirections:UIPopoverArrowDirectionLeft animated:YES];
  }else{
    [_popover setContentViewController:tableViewController];
  }
}
Typescript answered 19/9, 2014 at 18:30 Comment(0)
S
0

Add these two methods into your UIwebview main class. add <UIPopoverPresentationControllerDelegate> in your webview interface.h class as well. define .

@property (strong, nonatomic) UIPopoverPresentationController *pop;

then

@synthesize pop;

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    // Assuming you've hooked this all up in a Storyboard with a popover presentation style
    if ([segue.identifier isEqualToString:@"showPopover"]) {
        UINavigationController *destNav = segue.destinationViewController;
        pop = destNav.viewControllers.firstObject;

        // This is the important part
        UIPopoverPresentationController *popPC = destNav.popoverPresentationController;
        popPC.delegate = self;
    }
}
- (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller {
    return UIModalPresentationNone;
}
Samothrace answered 7/10, 2014 at 10:54 Comment(0)
K
0

You have already have the content view controller set:

_popover = [[UIPopoverController alloc] initWithContentViewController:tableViewController]; 
                                                                        ↑
                                                                      Here

So, why not just delete the line?:

 [_popover setContentViewController:tableViewController];

That should work.

Knocker answered 19/10, 2014 at 19:52 Comment(0)
E
0

If popover is not visible when you are calling [_popover setContentViewController:tableViewController];, app will get crash.

Because this method should be called when popover is visible on the screen.

Make sure your popover is visible,

if(_popover != nil && [_popover isPopoverVisible] == YES)
{
   [_popover setContentViewController:tableViewController];
}else
{
       //create new popover object if _popover is nil or present it
}
Equity answered 24/10, 2014 at 14:6 Comment(0)
K
0
   [self.popover dismissPopoverAnimated:YES]; //in case it's already showing.  
   self.popover = nil; // N.B. this is not the same as popover = nil. 
   self.popover = [[UIPopoverController alloc] initWithContentViewController:tableViewController];
   [self.popover  presentPopoverFromRect:CGRectMake(self.textField.frame.size.width /
   2, self.textField.frame.size.height / 1, 1, 1) inView:self.textField
   permittedArrowDirections:UIPopoverArrowDirectionLeft animated:YES];
Knockwurst answered 1/12, 2014 at 2:6 Comment(0)
K
0

You do not need to create new instance of UIPopoverController neither set new contentViewController property after you first present UIPopoverViewController. (it depends on how you dismiss you popoverController)

However contentViewController can't be changed before popoverController presentation.

To workaround check popoverController.contentViewController properties. If it is nil, set conntentViewController, otherwise just present popover.

If you want to change contentViewController do it after presentation: use setContentViewController:animated: method. Check that popoverController.isPopoverVisible before call this method.

Keynesianism answered 27/1, 2015 at 11:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.