iOS crash when pressing button from manually loaded viewcontroller - objective-c

I programmatically instantiate a UIViewController from a xib, add it as a child VC to an existing main view, then add the child VC's view (which contains a button) to my main view. The view with the button displays just fine, but when I press the button, the app crashes with an "unrecognized selector" message.
I created the XIB by creating a custom subclass of a UIViewController ( File - New - Cocoa Touch class, selecting a subclass of UIViewController, and asking Xcode to also generate the XIB file). I then placed a button on that XIB, and hooked it up to the newly created UIViewController subclass. The XIB file's owner is set to the name of the new class.
So it looks to me like the ButtonPushed action message doesn't get send to my custom UIVC, and is going instead to the generic UIViewController - which rightly doesn't understand the ButtonPushed message.
How do I make sure that the action message sent when the button is pressed in fact goes to my custom UIViewController?
This is the code in my awakeFromNib method for the main view (main view is from Main.storyboard):
UIViewController *vc2 = [[UIViewController alloc] initWithNibName:#"ButtonVC" bundle:nil];
vc2.view.frame = CGRectMake(90, 140, 50, 200); //dont obscure the main view completely.
[self addChildViewController:vc2];
[self.view addSubview:vc2.view]; //vc2.view has a button, which shows up fine
[vc2 didMoveToParentViewController:self];
I've managed to get the equivalent functionality by creating a storyboard just with the button, and programmatically loading that, then instantiating my buttonViewController, adding it and its view as a child VC to my main view. That works just fine, but it seems to me like I should be able to achieve this with "only" a XIB.

Try this:
UIViewController *vc2 = [[ButtonVC alloc] initWithNibName:#"ButtonVC" bundle:nil];
And also make sure that the class in your xib for the view controller is set properly (see my answer here)

Related

View Controller Doesn't respond to Action Methods

I have a simple test app (in OSX) that has a view controller with a button in its view. The button's action method is in the view controller's class, and that IBAction is connected in IB (through File's Owner). When the button is clicked, I get an EXC_BAD_Access error (except occasionally I get -[NSRunLoop buttonClick:] instead). I've read a bunch of posts here on SO having to do with NSViewControllers not being in the responder chain, but also that specifically hooking the action method up in IB should work. The only code I have is this:
In the app delegate:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
TestController *controller = [[TestController alloc] initWithNibName:#"TestController" bundle:nil];
[self.window.contentView addSubview:controller.view];
}
And, in the TestController class, just this:
-(IBAction)buttonClick:(id)sender {
NSLog(#"%#",sender);
}
I have 2 questions. Why is this happening, and where is the correct mvc place to put IBActions for button methods (shouldn't controller classes handle these)?
First off, I believe in the App Delegate there is already a property called viewController already defined for you. You should use this code self.viewController = [[TestController alloc] initWithNibName:#"TestController" bundle:nil]; instead of TestController *controller = [[TestController alloc] initWithNibName:#"TestController" bundle:nil];. If that doesn't fix it, then I'm not sure what's wrong. Your code should return the attributes of the button you clicked. I created my own sample project and tested out your code (although my app delegate looked different than yours) and it worked fine.
Second, you should elaborate on your second question "Why is this happening, and where is the correct mvc place to put IBActions for button methods (shouldn't controller classes handle these)?". It's not very clear what you mean.
Hope this helps a little.
NSViewController will not respond to IBActions on mac os x on iPhone It is the correct place to put your IBAction as the view should only draw its content (data) not change it. But on Mac os x the NSViewController is for setting up the NSView it's no ment to respond to IBActions you have two choices one is to put your IBAction in your NSView or is create a NSWindowController .
On mac osx you have plenty of screen space and you will alway have views within a window you'll use NSViewController to add them to your window and to setup your view but the window is first in your responder chain then your NSWindowController . eg: you may have one window and two view controller showing the same view which may have 5 test fields in it and view controller one loads that with data but you can not edit the data then view controller two loads the same view with the same data but enable editing for the text fields so. but all action methods will go to the WindowController .
I recommend you to check your viewController's identity inspector.
It may indicate wrong custom class.
viewController created basically usually has name ViewController on the section.

Modal UIViewController will not push to next UIViewController

The start of the structure is as follows...
UITabBarController -> UINavigationController(s)
From each of the UINavigationControllers, I have a UIBarButtonItem that modally presents a UIViewController.
This UIViewController has a MKMapView with pins at multiple locations. When clicked, they display an annotation with a disclosure button.
Within this UIViewController, it is my intention to push a detail page (UITableViewController) when pressing the disclosure button of the annotation. The method calloutAccessoryControlTapped: receives the appropriate pin, but the transition to the next controller fails.
I have tried every combination of the following methods...
[self.navigationController ...]
[self.parentViewController ...]
[self.parentViewController.navigationController ...]
with the method being either...
presentModalViewController:
pushViewController:
I have done all of these with the UIViewController being on its own, and also with it embedded inside of a UINavigationController.
All of these properties return null...
self.navigationController
self.parentViewController
self.parentViewController.navigationController
This is the first time I've used storyboard for an Xcode project. Am I missing a step?
Try getting rid of the code and implementing the transitions in storyboard by control dragging from the button to the view controller you wish to load modally. When the "Storyboard Segue" menu pops up select "modal". In the modal view controller, I like to use code to return from the modal by calling:
[self dismissModalViewControllerAnimated:YES];
To Presenting Storyboard View Controllers Programmatically scroll to that section in gravityjack on the link provided.
For example, I have a view controller that I created in storyboard which I can call programmatically with the following two statements:
SettingsViewController *settingsVC = [self.storyboard instantiateViewControllerWithIdentifier:#"settingsVC"];
[self.navigationController pushViewController:settingsVC animated:YES];

View Controller behaves differently when set as 'initial view controller' vs. loading with presentModalViewController

My app has a map that tracks the user's location. This map will only appear under certain circumstances, and will dominate the user's attention until a particular task is complete, which is why the map isn't part of a navigation or tab bar UI.
If my map VC is set as the initial view controller in storyboard, it works fine. But if I try to load the map VC from elsewhere like this;
MapViewController *mapVC = [[MapViewController alloc] init];
[self presentModalViewController:mapVC animated:YES];
I just get a black screen.
I can confirm with NSLog that the VC is calling viewDidLoad and viewDidAppear, but the 'map' property of the VC is (null). I don't understand why (or how) I need to create the map property manually when using this technique, but it gets done for me when it is the initial VC.
The MapViewController instance in your storyboard is configured with a view hierarchy, including an MKMapView, and whatever else you did to configure that particular instance in the storyboard.
Now in this code which you show here, you are creating a completely new instance of MapViewController. It has no relationship to the instance in the storyboard other than they happen to be of the same class. So the one you create here with [[MapViewController alloc] init] has no view hierarchy (which is why you see a black screen), and none of the outlets or other configuration you may have made to the other MapViewController in your storyboard.
So what you want is to load that MapViewController that you've already set up from the storyboard. Assuming you are doing this from within a method in another view controller loaded from the same storyboard already, you can just do this:
// within some method on another vc from a scene in the same storyboard:
// given an identifier for the map view controller we want to load:
static NSString *mapVCIdentifier = #"SomeAppropriateIdentifier";
NSLog(#"Storyboard: %#",self.storyboard); // make sure this vc(self) was loaded from a storyboard
MapViewController *mapVC = [self.storyboard instantiateViewControllerWithIdentifier:mapVCIdentifier];
[self presentModalViewController:mapVC animated:YES];
And then back in the storyboard, just make sure you set the identifier for this map view controller to "SomeAppropriateIdentifier".
Hope that helps.

How do I make a reusable XIB with it's own UIViewController?

I am trying to create a reusable "picker". It is basically like a keypad of a phone. Since I will be using this a lot in my iPhone app, I have been frustrated with trying to make it appear.
It is in its own XIB file and has its own UIViewController subclass as the FileOwner.
However, when I instantiate this with:
MonthPickerViewController *mpvc
= [[MonthPickerViewController alloc] initWithNibName:#"MonthPicker"
bundle:nil];
Nothing happens on the screen. Yet is does fire the -viewWillAppear methods, etc.
So, what am I doing wrong either in code or in InterfaceBuilder that is preventing my view to appear?
Are you pushing the view controller?
[[self navigationController] pushViewController:mpvc animated:YES];
Or are you adding the view controller's view as a subView of your current view?
First, make sure you've hooked everything up right inside Interface Builder. An easy gotcha is to forget to connect the View object up to the view outlet of your UIViewController subclass.
Then, as Adam says, you need to actually display the view. Assuming you're doing this inside the code of another view controller, you'd need something like the following if you just wanted the new view to appear ontop of your current view:
[self.view addSubview:mpvc.view];
Of if you are using a navigation controller to stack views:-
[[self navigationController] pushViewController:mpvc animated:YES];

UINavigationController Push Views

Sorry - this may be an easy question, I'm new to iPhone development and still wrapping my head around Views vs ViewControllers.
I have a NavigationViewController and I can push Views using the following method in the RootViewController which is connected to a Bar Button Item:
- (IBAction)switch:(id)sender {
NSLog(#"Swith...");
LibraryViewController *varLibraryViewController = [[LibraryViewController alloc] initWithNibName:#"LibraryViewController" bundle:nil];
[[self navigationController] pushViewController:varLibraryViewController animated:YES];
}
I want to call this same method from a button on the same view that is currently loaded. Basically I want to have the Bar Button at the top call the same method as a button on the view. I was wondering how to call a method in the ViewController from the view loaded from that viewController. Hopefully that makes sense.
Do I need to create an instance of the RootViewController? I would think that would already be instantiated. Thank you.
BTW, the code you have pasted there is leaking your LibraryViewController. You need to either explicitly release it after pushing it, or autorelease it when it's created.
Your RootViewController should have its own xib file. In this xib, the RootViewController is represented by the object named "File's Owner". You can link buttons on the view to File's Owner the same way you can link things to RootViewController in MainMenu.xib.
You'll want to declare your method as an IBAction in your header file:
- (IBAction) myMethod: (id) sender;
Save your header, then switch to Interface Builder. Right click on the Bar Button, and drag from the selector tag to your view controller object (probably the File Owner). When you release, you should be given a popup menu of available actions, and myMethod should be selectable.
If you don't get this popup, you may need to make sure your File Owner class is set properly: select the File Owner in the file window, then select "Tools" > "Identity Inspector" from the menu. In the inspector, type your view controller's class into the Class field.