jason white
jason white

Reputation: 711

UIViewController view error

I have an B UIViewController and there's close button inside.

        -(IBAction) close:(id) sender 

From A viewcontroller I present the B viewcontroller like this

  [self presentViewController:B animated:YES completion:NULL];

then everything is OK, I can hit the close button inside B.

However if I do

   B* bcontrol=[[B alloc] init];
   [self.view addsubview bcontrol.view];

then this way, if I hit the close button, it generate EXEC_BAD_ACCESS error.

why is that? any ideas?

Upvotes: 0

Views: 225

Answers (2)

Rob
Rob

Reputation: 437542

You don't provide the code for your close method. The close method code will have to change to correspond how you transitioned to this new view. If you got to view controller B via presentViewController you would then presumably return back to A via:

- (IBAction)close:(id)sender
{
    [self dismissViewControllerAnimated:YES completion:nil];
}

You then consider if you go to the B controller via

B* bcontrol=[[B alloc] init];
[self.view addsubview bcontrol.view];

Clearly, the dismissViewControllerAnimated won't work anymore. So the question is what you changed your close method to.

But before I get to that, there are two problems with this construction. First, if you do this in a non-ARC project, you'll leak your controller. Worse, if you do this with ARC, you will get exceptions because the controller will be released when it falls out of scope (you'll see meaningful errors if you turn on zombies). The typical answer for this problem is to making the B* bcontrol and ivar of A, or do something like this so the pointer to bcontrol doesn't get released on you. This solves the immediate problem.

Second, the bigger problem is that this whole construct (adding a controller's view as a subview of another view) is generally not a good idea because your view controller hierarchy and your view hierarchy are out of sync. (For a lengthy discussion of these issues, watch WWDC 2011 session 102 on View Controller Containment.) You might not get rotation events. You don't know what else will happen (because iOS is assuming it can navigate the view controller hierarchy in order to get messages to the various controllers). Bottom line, it's not a good idea to take another view controller's view and just add it as a subview of your current view. The only time you do this is if you're doing genuine view controller containment and have done addChildViewController and didMoveToParentViewController and you just want to get the first view up (see the aforementioned video).

But, if you're determined to do the [self.view addSubview:bcontrol.view]; construct, then clearly you can't use the same dismissViewControllerAnimated in you B's close method. The logical way to remove B's view from A's view would be [self.view removeFromSuperView];. But then you have a challenge of how to make sure that your A class now cleans up that B instance you originally created, or else you'll have a leak.

Bottom line, this isn't a good idea. Stick with presentViewController (which you'll dismiss with dismissViewControllerAnimated) or pushViewController (which you'll dismiss with popViewControllerAnimated) or use view controller containment. Given that you discuss the idea of a "close" button, that implies a UI that really lends itself to either the presentViewController or pushViewController models.

Upvotes: 0

Phillip Mills
Phillip Mills

Reputation: 31016

You're using ARC, I assume.

You create bcontrol as a local variable and don't keep a reference to it, so ARC releases it when you strip out its view. Then your button tries to trigger an action in a deallocated object...and you know the rest.

Make B a strong property so that it stays around while you need it's view.

Upvotes: 1

Related Questions