thegrinner
thegrinner

Reputation: 12241

App crashes in viewWillAppear of UISplitViewController

I'm working on an iOS app that uses a tab bar to swap between subclasses of a subclass of UISplitViewController. Up until the iOS 8 GM seed (and release version), that was working fine.

However, when building with the iOS 8 SDK for an iPad running iOS 8, I am continually getting a crash when switching to one (and only one) of the view controller subclasses. This crash drops down into the viewWillAppear method of the superclass (UISplitViewController), and has something to do with a Popover being set up in that method:

2014-09-29 09:24:13.526 AppName[51815:2369763] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UIPopoverController initWithContentViewController:] must not be called with `nil`.'
*** First throw call stack:
(
    0   CoreFoundation                      0x0363adf6 __exceptionPreprocess + 182
    1   libobjc.A.dylib                     0x031fda97 objc_exception_throw + 44
    2   CoreFoundation                      0x0363ad1d +[NSException raise:format:] + 141
    3   UIKit                               0x012eaabe -[UIPopoverController _initWithContentViewController:popoverControllerStyle:] + 436
    4   UIKit                               0x0111eb43 -[UISplitViewController _setupHiddenPopoverControllerWithViewController:] + 122
    5   UIKit                               0x0111ed82 -[UISplitViewController _viewControllerHiding:] + 322
    6   UIKit                               0x0112556a -[UISplitViewController viewWillAppear:] + 189
    7   AppName                             0x001ac9c0 -[BaseSplitViewController viewWillAppear:] + 624
    8   AppName                             0x00342b82 -[HomeGlanceController viewWillAppear:] + 306
    9   UIKit                               0x00d7914f -[UIViewController _setViewAppearState:isAnimating:] + 545
    10  UIKit                               0x00d796ca -[UIViewController __viewWillAppear:] + 148
    11  UIKit                               0x00d910b1 -[UIViewController(UIContainerViewControllerProtectedMethods) beginAppearanceTransition:animated:] + 200
    12  UIKit                               0x00dc2dd3 -[UITabBarController transitionFromViewController:toViewController:transition:shouldSetSelected:] + 619
    13  UIKit                               0x00dc2352 -[UITabBarController transitionFromViewController:toViewController:] + 64
    14  UIKit                               0x00dbe545 -[UITabBarController _setSelectedViewController:] + 340
    15  UIKit                               0x00dbe3c7 -[UITabBarController setSelectedViewController:] + 193
    16  UIKit                               0x00dc222b -[UITabBarController _tabBarItemClicked:] + 326
    17  libobjc.A.dylib                     0x032137cd -[NSObject performSelector:withObject:withObject:] + 84
    18  UIKit                               0x00c1f79d -[UIApplication sendAction:to:from:forEvent:] + 99
    19  UIKit                               0x00c1f72f -[UIApplication sendAction:toTarget:fromSender:forEvent:] + 64
    20  UIKit                               0x00f6228d -[UITabBar _sendAction:withEvent:] + 466
    21  libobjc.A.dylib                     0x032137cd -[NSObject performSelector:withObject:withObject:] + 84
    22  UIKit                               0x00c1f79d -[UIApplication sendAction:to:from:forEvent:] + 99
    23  UIKit                               0x00c1f72f -[UIApplication sendAction:toTarget:fromSender:forEvent:] + 64
    24  UIKit                               0x00d52a16 -[UIControl sendAction:to:forEvent:] + 69
    25  UIKit                               0x00d52e33 -[UIControl _sendActionsForEvents:withEvent:] + 598
    26  UIKit                               0x00d52a4e -[UIControl sendActionsForControlEvents:] + 48
    27  UIKit                               0x00f670b1 -[UITabBar(Static) _buttonUp:] + 123
    28  libobjc.A.dylib                     0x032137cd -[NSObject performSelector:withObject:withObject:] + 84
    29  UIKit                               0x00c1f79d -[UIApplication sendAction:to:from:forEvent:] + 99
    30  UIKit                               0x00c1f72f -[UIApplication sendAction:toTarget:fromSender:forEvent:] + 64
    31  UIKit                               0x00d52a16 -[UIControl sendAction:to:forEvent:] + 69
    32  UIKit                               0x00d52e33 -[UIControl _sendActionsForEvents:withEvent:] + 598
    33  UIKit                               0x00d5209d -[UIControl touchesEnded:withEvent:] + 660
    34  UIKit                               0x00c6faba -[UIWindow _sendTouchesForEvent:] + 874
    35  UIKit                               0x00c70595 -[UIWindow sendEvent:] + 791
    36  AppName                             0x004527b4 -[LHCWindow sendEvent:] + 100
    37  UIKit                               0x00c35aa9 -[UIApplication sendEvent:] + 242
    38  UIKit                               0x00c458de _UIApplicationHandleEventFromQueueEvent + 20690
    39  UIKit                               0x00c1a079 _UIApplicationHandleEventQueue + 2206
    40  CoreFoundation                      0x0355e7bf __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 15
    41  CoreFoundation                      0x035542cd __CFRunLoopDoSources0 + 253
    42  CoreFoundation                      0x03553828 __CFRunLoopRun + 952
    43  CoreFoundation                      0x035531ab CFRunLoopRunSpecific + 443
    44  CoreFoundation                      0x03552fdb CFRunLoopRunInMode + 123
    45  GraphicsServices                    0x0567524f GSEventRunModal + 192
    46  GraphicsServices                    0x0567508c GSEventRun + 104
    47  UIKit                               0x00c1de16 UIApplicationMain + 1526
    48  AppName                             0x0004c3de main + 222
    49  libdyld.dylib                       0x03b09ac9 start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException

I found that the ViewController in question has three subviews the first time it is shown, but when I remove either of the two that share a frame (by removing the self.view addSubview calls) the crash is still there:

UIImageView, {{50, 10}, {260, 490}}
UIView, {{50, 10}, {260, 490}}
UIView, {{50, 510}, {670, 390}}

What would cause a popover related crash in a subclass of UISplitViewController?


This is the viewWillAppear: in the BaseSplitViewController. Running an exception breakpoint stops on the [super viewWillAppear:animated] line.

- (void)viewWillAppear:(BOOL)animated {
    iPadAppDelegate *appDelegate_iPad = [Utils getAppDelegateForDeviceType:DEVICE_TYPE_IPAD];
    if(![appDelegate_iPad getIsTabInitialised]) {
        return ;
    }
    DDLogVerbose(@"%@ appearing",[self class]);

    // Which tab is opening? Log it!
    NSString *screen = [kAnalyticsNavigationPrefix stringByAppendingString:NSStringFromClass([self class])];
    [AnalyticsManager logEvent:screen];

    // Ensure we don't cover anything with the status bar in iOS 7. It looks like
    // the orientation code wasn't being called anymore?
    [self adjustControllerForOrientation:[self interfaceOrientation]];

    [super viewWillAppear:animated];
}

The BaseSplitViewController init and viewDidLoad - awakeFromNib is not overriden.

- (id)init {
    if (self = [super init]) {
        [self setInterfaceOrientation:[UIApplication sharedApplication].statusBarOrientation];

    }
    return self;
}

// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
    [super viewDidLoad];

    DDLogVerbose(@"%@ loaded",[self class]);
    isModalViewPresent = NO;
    self.view.backgroundColor = [UIColor whiteColor];

    if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0")) {
        self.edgesForExtendedLayout = UIRectEdgeNone;
    }

}

HomeGlanceController subclasses BaseSplitViewController (which subclasses the UISplitViewController). It is the only subclass of BSVC that crashes.

- (id)init {
    if (self = [super init]) {
        bIsAnyKeypadPresent = FALSE;
        //to create the favorite zone screen view
        favoriteScreenBackGroundView = [[UIView alloc] initWithFrame:CGRectMake(280, 0, 720, 430)];
        favoriteScreenbackGroundImageView = [[UIImageView alloc] initWithFrame:CGRectMake(40, 5, 667,405)];
        favoriteZoneScreenMainView                  = [[UIView alloc] initWithFrame:CGRectMake(20, 40 ,640,340)];

        favoriteKeypadScreenMainView = [[UIView alloc] initWithFrame:CGRectMake(10, 50 ,200,330)];

        // Initialize the layout for the Favorite Controller.
        [self initFavoriteLayoutCoordinates];
        UITabBarItem * tabBarItem = [[UITabBarItem alloc]initWithTitle:HomeGlanceTabTitle
                                                                 image:[Utils getImageNamed:HomeGlanceIcon] 
                                                                   tag:1];
        tabBarItem.selectedImage = [Utils getImageNamed:HomeGlanceSelectedIcon];

        self.tabBarItem = tabBarItem;
        tabBarItem = nil;

        // pick the co-ordinates of the favorite view from the layout array.
        if ([favControlCoordinatesArray count]>layoutID) {
            [self setControllerCoordinates:[favControlCoordinatesArray objectAtIndex:layoutID]];
        }
    }

    return self;
}

- (void) viewDidLoad {
    // I've tried commenting all but the super call out. The crash remains
    [self createImageViewForFavorites];

    // to create the View for favorite zone
    [self createBackGroundViewForFavoriteZoneScreen];
    [self createFavoriteZoneScreenToolbar];
    [self createFavoriteZoneScreenMainView];
    [self createFavoriteKeypadScreenToolbar]; 

    [super viewDidLoad];
}

- (void)viewWillAppear : (BOOL) animated {
    [super viewWillAppear:animated];

    [self startTimer];
    iPadAppDelegate *appDelegate_iPad = [Utils getAppDelegateForDeviceType:DEVICE_TYPE_IPAD];
    if(![appDelegate_iPad getIsTabInitialised])
    {
        return;
    }
    [self createFavoriteZoneScreenMainView];

    [self createFavoriteKeypadScreenMainView];

    // Snipped methods that don't impact the UI directly (commented out in this build)
}

Apparently the crash only occurs when the iPad is in (or turned to) portrait mode:

The end cause appears to be the same ([UIPopoverController initWithContentViewController:] must not be called with nil.).

Upvotes: 0

Views: 1551

Answers (1)

thegrinner
thegrinner

Reputation: 12241

It appears the default preferredDisplayMode is UISplitViewControllerDisplayModeAutomatic, and the UISplitViewController is doing something involving creating a popover controller in portrait when it has that display mode and it calls viewWillAppear or rotates.

To avoid the crash, I added code to change the display mode in the viewDidLoad method:

[self setPreferredDisplayMode:UISplitViewControllerDisplayModeAllVisible];

Upvotes: 4

Related Questions