views:

26

answers:

1

Hi all, i have some trivial question that is bothering me. When i create a new project using "Window-based Application" eg. for iPhone i have its app delegate declared, then for example i want to create one additional UIViewController/nib pair and add it to the main window view as kinda of "entering point view" - i'm trying to avoid to enforce the main app delegate to be the "view" place, and keep the delegate code clean of view staff.

Also that's bothers me, i just don't want to use "View-based Application" as starting template, as for learning, as for commodity, i want to be able to understand the problem and find the solution.

The problem is the following:

  1. I create new Project for example TestProject as Window-based application
  2. I have now TestProjectAppDelegate.m with entry point menthod that is well known ....
  3. I create a new UIViewController sub-class with coresponding nib for eg. TestView
  4. I add the TestView to the main window view and everything is fine, the view load and shows overlapping the main window view which is normal ...

so the main app delegate (TestProjectAppDelegate.m) looks something like:

#import "TestProjectAppDelegate.h"
#import "TestView.h"
...
- (BOOL)application:(UIApplication *)application .....
{    

TestView *vc = [[TestView alloc] initWithNibName:@"TestView" bundle:nil];
[window addSubview:vc.view];
[vc release];
[window makeKeyAndVisible];

return YES;
}

To this point everything looks fine ,,, Now supose i want one button in the TestView, I add it in the IB, declare the common propreties in the header, and link the IBAction and IBOutlet with the drag method. Now TestView.h becomes something like:

@interface TestView: UIViewController {
UIButton *myButton;
}

@property (nonatomic, retain) IBOutlet UIButton *myButton;

- (IBAction) youPressedMe:(id)sender;

@end

.. and the TestView.m that is kinda common ... (with youPressedMe) implemented ...

#import "TestView.h"

@implementation TestView

@synthesize myButton;

-(void)viewDidLoad 
{
   [super viewDidLoad];
   NSLog(@"TestView loaded ...");
   //the title changes ok!
   [myButton setTitle:@"Press me now!" forState:UIControlStateNormal]; 
}

-(IBAction)youPressedMe:(id)sender
{
   NSLog(@"You really pressed me!");
}

...
// other standard code inserted by template 
...

-(void)dealloc
{
   [myButton release];
   [super dealloc];
}

@end

Ok, so here the problems rises out. I definitely have loaded and successfully initialised the TestView because the button drawn on its nib (in IB) is showing properly when app launched. I'm even able to change button Title to something else (during runtime). This is showing that the reference to it through "myButton" is linked properly. Furthermore the youPressedMe method, when i press the button gets never called. When i press the button, I get the error and the app crashes. The error is showing unknown selector sent to instance ... in the fashion like:

 -[NSCFString youPressedMe:]: unrecognized selector sent to instance 0x6d23730
  2010-10-20 20:04:09.293 TestProject[4295:207] *** Terminating app due to uncaught     exception 'NSInvalidArgumentException', reason: '-[NSCFString youPressedMe:]: unrecognized selector sent to instance 0x6d23730' ..... 

I checked 1000 times and have setup and linked the IBAction of the button properly (to its owner, that is TestView nib) for sure. Now, when doing the same example with "View Based" template as starting point, in the similar fashion, everything works out instantly ...

A: 

The code here posted is an example of a common pitfall when handling views and its controllers. The addSubview is retaining the VIEW but not the CONTROLLER it belongs to. Furthermore there are various controller methods that pass the controller itslef as an argument, and sometimes one gets confused because of the simillarity of usage. The solution is really simple. Make vc an synthesized proprety, and let app delegate dealloc release it, using it as a local scope variable is not enough in this example, it needs to live enough so the view controller (when needed) can handle user input - in this exaple button press.

Cheers

Cocoa4ever
Hi, thank you much for your response, yes it's true, after thinkering about it a bit i missused the view's controller view and forgot about the controller itself. That's the reason setting the Title on button worked (that's view job), but the touches caused app crash (that's controller job) because it was released. And it's really true, for example table view controllers use the whole controller as an argument eg. with: pushViewController: and eg. presentModalViewController: for modal views. Will take care of it much more in the future.