Search code examples
iosuisplitviewcontrollerxibuigesturerecognizernib

Why does creating a UIGestureRecognizer subclass in an nib cause a crash using a UISplitViewController


I was interested in using a UIGestureRecognizer subclass (UILongPressGestureRecognizer) in a ViewController Subclass I had. My ViewController has a UIToolBar and programmatically I can create a UILongPressGestureRecognizer instance attached to the UIToolBar and have everything work. My ViewController adopts the UIGestureRecognizerDelegate protocol. In the ViewController I have:

//My ViewController.h
@interface MyViewController: UIViewController<UIGestureRecognizerDelegate>
/* ... */

@property (nonatomic, retain) IBOutlet UIToolbar *toolbar;
@property (nonatomic, retain) IBOutlet UILongPressGestureRecognizer *longPressGesureRecognizer;

/* ... */

- (IBAction)handleGesture;

@end

The following code then works:

- (void)viewDidLoad
{
    [super viewDidLoad];

    //configure UILongPressGestureRecognizer
    if(self.longPressGesureRecognizer == nil){
        UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture)];

        longPress.minimumPressDuration = 1.5;
        longPress.numberOfTouchesRequired = 1;
        longPress.delegate = self;

        self.longPressGesureRecognizer = longPress;
        [self.toolbar addGestureRecognizer:self.longPressGesureRecognizer];

        [longPress release];
    }   
}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{    
    return YES;
}

- (IBAction)handleGesture{
    NSLog(@"Got a gesture!");
}

I didn't think I really need the UILongPressGestureRecognizer in my View Controller. When I tried to create it entirely in the MyViewController.xib file I did the following using XCode 4.2

  • Draged a UILongPressRecognizer on my UIToolBar in the xib (attached to toolBar in MyViewController).
  • Set the UILongPressRecognizer's delegate property to the File's Owner
  • Set the selector property to MyViewController's handleGesture message.

When I go to run the Application in the Simulator I crash which the following gdb error in the console:

 *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UILongPressGestureRecognizer initWithCoder:]: unrecognized selector sent to instance

Clearly UILongPressGestureRecognizer initWithCoder: isn't impelmented but I'm wondering what the correct way of setting up the UIGestureRecognizer is in the xib file. XCode has gestures there and I've connected it a view. I don't see what I'm going wrong. All links I've seen on UIGestureRecognizer including Apple's UIGestureRecognizer's class reference always uses the UIGestureRecognizer from code. I haven't seen an example using just the xib file to configure it.

I'm not too concerned with this issue because I can handle it with code in the ViewController but I'm confused why XCode allows the Gesture Recognizers to be added in the nib if UILongPressGestureRecognizer and/or UIGestureRecognizer don't conform to the NSCoding Protocol and initWithCoder will be called. My intuition says the problem is on my end on not Apple's but I'd love to understand what's going wrong.

Thanks!

Update

I seem to only see this behavior in testing when I use a UISplitViewController. If I create a ViewController based project the UIGestureRecognizer works as expected. If I add a a Gesture Recognizer onto the DetailView of a UISplitViewController app I get a crash like this:

2011-11-03 10:17:55.873 GestureSplitViewTest[1143:b603] -[UILongPressGestureRecognizer initWithCoder:]: unrecognized selector sent to instance 0x59696b0
2011-11-03 10:17:55.877 GestureSplitViewTest[1143:b603] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UILongPressGestureRecognizer initWithCoder:]: unrecognized selector sent to instance 0x59696b0'
*** Call stack at first throw:
(
    0   CoreFoundation                      0x011b15a9 __exceptionPreprocess + 185
    1   libobjc.A.dylib                     0x01305313 objc_exception_throw + 44
    2   CoreFoundation                      0x011b30bb -[NSObject(NSObject) doesNotRecognizeSelector:] + 187
    3   CoreFoundation                      0x01122966 ___forwarding___ + 966
    4   CoreFoundation                      0x01122522 _CF_forwarding_prep_0 + 50
    5   UIKit                               0x002fe9fd UINibDecoderDecodeObjectForValue + 2592
    6   UIKit                               0x002fe2f5 UINibDecoderDecodeObjectForValue + 792
    7   UIKit                               0x002ff6ac -[UINibDecoder decodeObjectForKey:] + 398
    8   UIKit                               0x00214979 -[UIRuntimeConnection initWithCoder:] + 212
    9   UIKit                               0x003d34a8 -[UIRuntimeOutletCollectionConnection initWithCoder:] + 64
    10  UIKit                               0x002fe9fd UINibDecoderDecodeObjectForValue + 2592
    11  UIKit                               0x002fe2f5 UINibDecoderDecodeObjectForValue + 792
    12  UIKit                               0x002ff6ac -[UINibDecoder decodeObjectForKey:] + 398
    13  UIKit                               0x00213c36 -[UINib instantiateWithOwner:options:] + 804
    14  UIKit                               0x00215ab7 -[NSBundle(UINSBundleAdditions) loadNibNamed:owner:options:] + 168
    15  UIKit                               0x000cb628 -[UIViewController _loadViewFromNibNamed:bundle:] + 70
    16  UIKit                               0x000c9134 -[UIViewController loadView] + 120
    17  UIKit                               0x000c900e -[UIViewController view] + 56
    18  UIKit                               0x000c7482 -[UIViewController contentScrollView] + 42
    19  UIKit                               0x000d7f25 -[UINavigationController _computeAndApplyScrollContentInsetDeltaForViewController:] + 48
    20  UIKit                               0x000d6555 -[UINavigationController _layoutViewController:] + 43
    21  UIKit                               0x000d7870 -[UINavigationController _startTransition:fromViewController:toViewController:] + 524
    22  UIKit                               0x000d232a -[UINavigationController _startDeferredTransitionIfNeeded] + 266
    23  UIKit                               0x001ed2e9 -[UILayoutContainerView layoutSubviews] + 226
    24  QuartzCore                          0x01888a5a -[CALayer layoutSublayers] + 181
    25  QuartzCore                          0x0188addc CALayerLayoutIfNeeded + 220
    26  QuartzCore                          0x018300b4 _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 310
    27  QuartzCore                          0x01831294 _ZN2CA11Transaction6commitEv + 292
    28  UIKit                               0x0001b9c9 -[UIApplication _reportAppLaunchFinished] + 39
    29  UIKit                               0x0001be83 -[UIApplication _runWithURL:payload:launchOrientation:statusBarStyle:statusBarHidden:] + 690
    30  UIKit                               0x00026617 -[UIApplication handleEvent:withNewEvent:] + 1533
    31  UIKit                               0x0001eabf -[UIApplication sendEvent:] + 71
    32  UIKit                               0x00023f2e _UIApplicationHandleEvent + 7576
    33  GraphicsServices                    0x00eb2992 PurpleEventCallback + 1550
    34  CoreFoundation                      0x01192944 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 52
    35  CoreFoundation                      0x010f2cf7 __CFRunLoopDoSource1 + 215
    36  CoreFoundation                      0x010eff83 __CFRunLoopRun + 979
    37  CoreFoundation                      0x010ef840 CFRunLoopRunSpecific + 208
    38  CoreFoundation                      0x010ef761 CFRunLoopRunInMode + 97
    39  UIKit                               0x0001b7d2 -[UIApplication _run] + 623
    40  UIKit                               0x00027c93 UIApplicationMain + 1160
    41  GestureSplitViewTest                0x0000234a main + 170
    42  GestureSplitViewTest                0x00002295 start + 53
)
terminate called throwing an exceptionsharedlibrary apply-load-rules all
Current language:  auto; currently objective-c
(gdb)

Solution

  • Have you made sure that you are not using iOS 4.3 SDK instead of iOS 5 SDK?

    I spent a good 30 minute to find out I was using the wrong SDK.