How can I get a reference from UIBarButtonItem to the containing navigationController? - objective-c

I am in a barButtonItem handler method, and I need the reference to the navigation controller. But I do not have a stored reference to navigation controller. Any idea?
#import "ReportViewController.h"
#import "CenterViewAnimationUtility.h"
#implementation ReportViewController
- (void)viewDidLoad
{
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button addTarget:[CenterViewAnimationUtility class] action:#selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside];
}
//next file ..
#import "CenterViewAnimationUtility.h"
#implementation CenterViewAnimationUtility
+ (void)buttonPressed:(UIBarButtonItem *)barButtonItem {
UINavigationController *navigationController = barButtonItem.//..
}

Assuming this method is in a view controller which is in the navigation controller you can simply do:
UINavigationController *navigationController = self.navigationController;
Question - why is your button handler a class method? This solution only works if you make the button handler a proper instance method.
If you REALLY need the target to be a class method, you can get a reference to the view controller from the button's target property:
UIViewController *target = barButtonItem.target;
UINavigationController *navigationController = target.navigationController;
Update:
Based on the fact that in this case the button's target is not the view controller, this solution does not work.

Do I understand correctly that you're making it a class method because it is used on a variety of buttons from different view controllers?
If so, a work-around would be to have each button send a target/action message to a method within its own UIViewController, and then have that method call the common class method, passing the UINavigationController as a parameter.
UINavigationController *navController = self.navigationController;
[<classObject> buttonPressed:(UIBarButtonItem *)barButtonItem
fromNavigationcontroller:(UINavigationController *)navController;
You would obviously need to re-write the class method to accept the additional parameter:
+ (void)buttonPressed:(UIBarButtonItem *)barButtonItem
fromNavigationcontroller:(UINavigationController *)navController{

Related

How do I implement a UINavigationController in this case?

current version of my project :
I have 5 different UIViewControllers in my app. I've set my
FirstViewController to be the Initial View Controller using the
Attributes Inspector. I move back and forth from one ViewController to
another by using buttons to which I assign modal segues, from one
ViewController to another, using the StoryBoard
What I want to change:
I want to keep the navigation buttons obviously, delete the modal segues and use
a UINavigationController instead. If I understand the concept
correctly, when using a UINavigationController I need to go into each
UIButton-IBAction and at the very end of the method I have to push the next
ViewController I want to move to, onto my NavigationController (do I also
have to pop the current one first?). However, I can't figure out how
to implement all that correctly.
What I've done so far:
I removed all modal segues from the storyboard and kept the navigation buttons along with their corresponding IBActions
I unchecked the box in the Attributes Inspector that was making my FirstViewController the initial View Controller of my app
I went into my AppDelegate.m and tried to create the Navigation Controller there and make my FirstViewController be the RootViewController
MyAppDelegate.m
-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UIViewController *myFirstViewController = [[FirstViewController alloc] init];
UINavigationController *myNavigationController = [[UINavigationController alloc] initWithRootViewController:myFirstViewController];
[myNavigationController pushViewController:myFirstViewController animated:YES];
// Override point for customization after application launch.
return YES;
}
I then tried to test if the above was working by going into the IBAction of a
navigation button on my FirstViewController and implemented the
following in order to move to my SecondViewController when the
button is pressed :
FirstViewController.m
- (IBAction)goRightButton:(UIButton *)sender
{
// some code drawing the ButtonIsPressed UIImageView on the current View Controller
UIViewController *mySecondViewController = [[SecondViewController alloc] init];
[self.navigationController pushViewController:mySecondViewController animated:YES];
}
but nothing happens. What am I doing wrong ?
You are not linking your XIB file. Please add your navigation controller as
UIViewController *myFirstViewController = [[FirstViewController alloc] initWithNibName:#"FirstViewController" bundle:nil];
navigationController = [[UINavigationController alloc] initWithRootViewController:myFirstViewController];
Use following code to move from one view to another
UIViewController *mySecondViewController = [[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
[self.navigationController pushViewController:mySecondViewController animated:YES];
If you are using a storyboard, you should just drag in the navigation controller there and hook it up to your app delegates. As long as it is the main storyboard, and you have identified a view controller to load first, you do not need to load any views in your app delegate.
In order to push a view programmatically that's in a storyboard, you need to do something like the following:
//bundle can be nil if in main bundle, which is default
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
MyCustomViewController *customVC = (MyCustomViewController *)[mainStoryboard instantiateViewControllerWithIdentifier:#"customVC"];
//standard way
[self.navigationController pushViewController:customVC animated:YES];
//custom animation
[UIView transitionWithView:self.navigationController.view duration:0.5 options:UIViewAnimationOptionTransitionCurlUp animations:^{
[self.navigationController pushViewController:customVC animated:NO];
} completion:nil];
You identify the view controller with the identifier you add in the storyboard editor. Below are some screenshots to help show what I mean.

Xcode 4 UIButton segue push to Table View Controller

Right now I have a UIButton setup in my storyboard that pushes to a Table View Controller. This is working as expected. What I am trying to do is have the UIButton load an xml file when it is pressed and still move onto the Table View Controller.
How can I do this? I already have the code the XML pieces, it's a question of where the code would go and how I would add the .h and .m files for a new ViewController subclass UIViewController. How do I link those files to the UIButton I've created in storyboard?
Drag your button connection to the new view and select custom Segue instead of Push or Modal.
Change the new custom Segue's class to "mySegueClass1" or what ever you'd like to call it.
Create a new Objective-C class with the same name as you just assigned to the custom segue.
Then inside your mySegueClass1.m file add the following code, and add what ever additional actions you want to -(void)perform
-(void)perform{
UIViewController *dst = [self destinationViewController];
UIViewController *src = [self sourceViewController];
[dst viewWillAppear:NO];
[dst viewDidAppear:NO];
[src.view addSubview:dst.view];
CGRect original = dst.view.frame;
dst.view.frame = CGRectMake(dst.view.frame.origin.x, 0-dst.view.frame.size.height, dst.view.frame.size.width, dst.view.frame.size.height);
[UIView beginAnimations:nil context:nil];
dst.view.frame = CGRectMake(original.origin.x, original.origin.y, original.size.height, original.size.width);
[UIView commitAnimations];
[self performSelector:#selector(animationDone:) withObject:dst afterDelay:0.2f];
}
- (void)animationDone:(id)vc{
UIViewController *dst = (UIViewController*)vc;
UINavigationController *nav = [[self sourceViewController] navigationController];
[nav popViewControllerAnimated:NO];
[nav pushViewController:dst animated:NO];
}
There is a method:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
//You can access the tableviewcontroller using segue.destinationViewController
//Maybe you want to set your model here.
}
That is called before a segue is made. Here you can do all the setup you need. You should place this method inside the controller that performs the segue, not the one receiving it. (In fact, probably xcode already put that piece of code for you).

view controller pushed from navigation controller is not displayed

I have a navigation view controller that pushes "viewController1" properly. Then, from "viewController1", goToApp function is called in order to push "appViewController". Function goToApp is executed but aplication remains at same view, "viewControlller1". How to push it? Thank you.
from viewController1:
navigationViewController *theInstance = [[navigationViewController alloc] init];
[theInstance goToApp];
in navigationViewController:
-(void)goToApp {
appViewController *AppsViewController = [[appViewController alloc] initWithNibName:#"appViewController" bundle:nil];
[[self navController] pushViewController:AppsViewController animated:YES];
[AppsViewController release];
}
goToApp executed but appViewController not launched.
You should not create a new UINavigationController in viewController1. You should get it through the property navigationController. That property will return the UINavigationController that has the UIViewController on it's stack (if any, so it can be nil).
UINavigationController * theInstance = self.navigationController;
[theInstance goToApp];
See: http://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIViewController_Class/Reference/Reference.html property navigationController.
Besides that, it is a better convention to name your pointers correctly to what they represent. I would suggest to rename theInstance to currentNavigationController or just navigationController.

UIPickerView Delegate and DataSource methods not getting called

I want to display PickerView when I click the button.
I have a view which has 3 buttons. In the corresponding actions to the buttons, I have alloc'ed and init'ed the pickerView as follows:
pickerView = [[UIPickerView alloc]init];
Also I have added the PickerView to the Subview as follows:
[self.view addSubview:pickerView];
I've also subclass'ed my class with UIPickerViewDataSource and UIPickerViewDelegate as follows:
MyClass : UIViewController
By NSLoging i saw that PickerView DataSource and Delegate methods are not getting called.
When I click(TouchUpInside) the button, the view shows up pickerView with origin=(0,0) which should be at bottom and pickerView appears total Black. I fixed the PickerView's frame by using:
pickerView.frame = CGRectmake(0,180,320,260);
I googled to check when the pickerView's DataSource and Delegate method are called but I couldn't find the proper answer.
I also tried the "hidden" property here as follow:
In viewDidLoad method:
pickerView.hidden = YES;
In the method which is called after clicking button:
pickerView.hidden = NO;
Help me please. I'm new to Objective C.
If you create your picker programmatically - did you actually set its delegate and dataSource?
// Assuming that delegate and data source is controller where picker is created
pickerView.delegate = self;
pickerView.dataSource = self;

"EXC_BAD_ACCESS" in switching to another view

I have MainMenuViewController with button which action is
- (IBAction) goToFirstView {
FirstViewController *fvc = [[FirstViewController alloc] init];
[self.view addSubview:fvc.view];
[fvc release];
}
FirstViewController have UIButton with action
- (IBAction) rightArrow {
SecondViewController *svc = [[SecondViewController alloc] init];
[self.view addSubview:svc.view];
[svc release];
}
But when I press "rightArrow" button app crashes with "EXC_BAD_ACCESS". Can't found my problem. Help me please.
[svc release];
The problem is here. When releasing the view controller, the view's events will target a freed object, and make your program crash (probably in viewDidLoad or viewDidAppear if it's instant but it doesn't matter). Note that a view does not (normally, AFAIK) retain it's view controller, if that might have been your assumption...
When you say [self.view addSubview:svc.view] you're adding SecondViewController's view to FirstViewController's view. Similar with MainViewController and FirstViewController. What you'll end up with is a view hierarchy that looks like this:
main view
first view
second view
I doubt that's really what you want. Instead, use a navigation controller with your MainViewController as the nav controller's root controller, and then use -pushViewController:animated: to push the controllers (not the views!) onto the navigation stack.