Calling Function from RootViewController - objective-c

How to call a RootViewController function from FirstViewController?
I'm using Xcode 4.6 with storyboard.
RootViewController.m:
-(void)openMenu
{
...
}
FirstViewController:
- (IBAction)btnMenu:(id)sender {
RootViewController *root = [[RootViewController alloc] init];
[root openMenu]; // No visible #interface for 'RootViewController' declares the selector 'openMenu'
}

You have to declare the method in your header RootViewController.h. Example
- (void)openMenu;

A common practice for something like this is to use delegation. Your FirstViewController would have a delegate and then your RootViewController would set the delegate for the instance, and receive the information for the event.
FirstViewController.h
#protocol FirstViewDelegate;
#interface FirstViewController : UIViewController
#property (strong) id<FirstViewDelegate> delegate;
#end
#protocol FirstViewDelegate <NSObject>
- (void)openMenu;
#end
FirstViewController.m
- (IBAction)btnMenu:(id)sender {
[self.delegate openMenu];
}
MainViewController.h
#import "FirstViewController.h"
#interface RootViewController : UIViewController
<
FirstViewDelegate
>
MainViewController.m
-(IBAction)showFirstViewButtonClicked:(id)sender {
FirstViewController *firstViewController = [[FirstViewController alloc] init];
firstViewController.delegate = self;
[self presentViewController:firstViewController animated:YES completion:nil];
}
-(void)openMenu {
// this will be called when the btnMenu action is fired in the firstViewController
}

Related

Objective-C Protocols Not Sending Message

I have read around, and it seems as though delegates would be really useful in my app. Unfortunately, every tutorial about protocols I have tried has failed - the delegate is not receiving the message! It would be great if someone could tell me what I'm doing wrong.
I created a really simple test app with two ViewControllers, a FirstViewController and a SecondViewController. I have set them up in container views to see the effect properly.
My Main.storyboard looks like this:
The purpose of the test app is to change the background colour of the SecondViewController when one of the buttons is pressed in the FirstViewController.
Here is FirstViewController.h:
#import <UIKit/UIKit.h>
#protocol FirstViewControllerDelegate
-(void)colourDidChange:(UIColor *)theColour;
#end
#interface FirstViewController : UIViewController{
UIButton *redButton;
UIButton *blueButton;
}
#property (nonatomic, retain) id <FirstViewControllerDelegate> delegate;
#property (nonatomic, retain) IBOutlet UIButton *redButton;
#property (nonatomic, retain) IBOutlet UIButton *blueButton;
-(IBAction)redPressed;
-(IBAction)bluePressed;
My FirstViewController.m:
#import "FirstViewController.h"
#interface FirstViewController ()
#end
#implementation FirstViewController
#synthesize redButton, blueButton;
#synthesize delegate;
-(IBAction)redPressed{
[self.delegate colourDidChange:[UIColor redColor]];
}
-(IBAction)bluePressed{
[self.delegate colourDidChange:[UIColor blueColor]];
}
-(void)viewDidLoad
{
[super viewDidLoad];
}
I think I have implemented the protocol and the calling of the delegate correctly.
Here is my SecondViewController.h:
#import <UIKit/UIKit.h>
#import "FirstViewController.h"
#interface SecondViewController : UIViewController <FirstViewControllerDelegate>
-(void)colourDidChange:(UIColor *)theColour;
And my SecondViewController.m:
-(void)colourDidChange:(UIColor *)theColour{
self.view.backgroundColor = theColour;
}
-(void)viewDidLoad
{
[super viewDidLoad];
FirstViewController *firstView = [[FirstViewController alloc]init];
firstView.delegate = self;
}
I have breakpointed the project and realised that colourDidChange: in the SecondViewController is never executed.
It would be much appreciated if someone could point out what I have done wrong, whether declaring (or conforming to) the delegate poorly or not setting the delegate the right way.
Many thanks.
I suspect that there are 2 instances of FirstViewController, one created by your storyboard and another one created in SecondViewController's viewDidLoad method.
When theFirstViewController creates SecondViewController it could set the delegate property or use an Outlet to connect them.
Note: delegate properties should not be retain, they should be assign (or weak with ARC).
You are honestly very close. Container views will call the prepareForSegue: method, so you should be initializing the second view controller's delegate in this method:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"TypeContainerViewSegueNameHere"]) {
SecondViewController *viewController = (SecondViewController *)segue.destinationViewController;
viewController.delegate = self;
}
}
This way you know that you are getting the instance of SecondViewController that will be in use. Also, you do not need to redeclare the delegate method in your SecondViewController.h file:
-(void)colourDidChange:(UIColor *)theColour;
Finally, in storyboard set the title of the container view segue to SecondViewController to whatever title you like and then copy paste that title to where 'TypeContainerViewSegueNameHere' is written above.
EDIT 1:
A typical situation would be similar to this:
#protocol ViewControllerDelegate;
#interface ViewController : UIViewController
#property (nonatomic, strong) id<ViewControllerDelegate>delegate;
#end
#protocol ViewControllerDelegate <NSObject>
- (void) delegateMethod;
#end
...
#implementation ViewController
- (void) buttonAction:(id)sender {
[self.delegate delegateMethod];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"TypeContainerViewSegueNameHere"]) {
SecondViewController *viewController = (SecondViewController *)segue.destinationViewController;
viewController.delegate = self;
}
}
#end
...
#interface SecondViewController : UIViewController <ViewControllerDelegate>
#end
...
#implementation SecondViewController
- (void)delegateMethod {
}
#end
That said, you could make your main view controller the delegate of your FirstViewController, which has the two view containers as seen in your screenschot. And then call a delegate method from the main view controller to the second view controller. Although I am curious as to why you have these two view controllers as child view controllers rather than placing a view and two buttons in one view controller.
EDIT 2:
Here is an example (written quickly and not tested). Think of it as a triangle of delegates:
#protocol FirstViewControllerDelegate;
#interface FirstViewController : UIViewController
#property (nonatomic, strong) id<FirstViewControllerDelegate>delegate;
#end
#protocol FirstViewControllerDelegate <NSObject>
- (void) firstViewControllerDelegateMethod;
#end
...
#implementation FirstViewController
- (void) buttonAction:(id)sender {
[self.delegate firstViewControllerDelegateMethod];
}
#end
...
#protocol MainViewControllerDelegate;
#interface MainViewController : UIViewController <FirstViewControllerDelegate>
#end
#protocol MainViewControllerDelegate <NSObject>
- (void) mainViewControllerDelegateMethod;
#end
...
#implementation MainViewController
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"TypeContainerViewSegueNameHere"]) {
SecondViewController *viewController = (SecondViewController *)segue.destinationViewController;
viewController.delegate = self.delegate;
}
if ([segue.identifier isEqualToString:#"TypeContainerViewSegueNameHere"]) {
FirstViewController *viewController = (FirstViewController *)segue.destinationViewController;
viewController.delegate = self;
}
}
- (void)firstViewControllerDelegateMethod {
[self.delegate mainViewControllerDelegateMethod];
}
#end
...
#interface SecondViewController : UIViewController <MainViewControllerDelegate>
#end
...
#implementation SecondViewController
- (void)mainViewControllerDelegateMethod {
}
#end
Like I said, you should think about reducing the complexity of this section of your app and consider putting all of your views in one view controller.

Objective C Protocol Delegate test

I'm studing protocol and delegates using an Example! When I try to re create this example I notice that this condition is not respected:
if([delegate respondsToSelector:#selector(amountEntered:)]) {blabla}
where is the mistake? Scripts:
First View .h
#import <UIKit/UIKit.h>
#import "EnterAmountViewController.h"
#interface DelegateExampleViewController : UIViewController <EnterAmountDelegate>{
IBOutlet UILabel *amountLabel;
}
-(IBAction)changeAmountPressed;
#end
First View .m
#import "DelegateExampleViewController.h"
#implementation DelegateExampleViewController
-(IBAction)changeAmountPressed
{
EnterAmountViewController * enterAmountVC = [[EnterAmountViewController alloc]init];
enterAmountVC.delegate = self;
}
-(void)amountEntered:(NSInteger)amount
{
amountLabel.text = [NSString stringWithFormat:#"%i" , amount];
}
#end
Second View .h
#import <UIKit/UIKit.h>
#protocol EnterAmountDelegate <NSObject>
-(void)amountEntered:(NSInteger)amount;
#end
#interface EnterAmountViewController : UIViewController {
IBOutlet UITextField *amountTextField;
id<EnterAmountDelegate> delegate;
}
-(IBAction)savePressed;
#property(nonatomic,retain) id<EnterAmountDelegate> delegate;
#end
Second View .m
#import "EnterAmountViewController.h"
#import "DelegateExampleViewController.h"
#implementation EnterAmountViewController
#synthesize delegate;
- (void)viewDidLoad {
[super viewDidLoad];
amountTextField.text = #"";
[amountTextField becomeFirstResponder];
}
-(IBAction)savePressed
{
if([delegate respondsToSelector:#selector(amountEntered:)])
{
[delegate amountEntered:[amountTextField.text intValue]];
NSLog(#"rugg");
}
}
#end
Thanks in advance!
In the method:
-(IBAction)changeAmountPressed
{
EnterAmountViewController * enterAmountVC = [[EnterAmountViewController alloc]init];
enterAmountVC.delegate = self;
}
you are creating an instance of EnterAmountViewController on the stack as a local variable. This variable will be inaccessible at the end of the scope. So, when you execute savePressed you are doing it on a different object where you did not set delegate.
In other words, when you check
if([delegate respondsToSelector:#selector(amountEntered:)])
it returns NO because delegate is nil...
The correct way to set the delegate is using the prepareForSegue mechanism:
#implementation DelegateExampleViewController
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
[(EnterAmountViewController*)segue.destinationViewController setDelegate:self];
}
...
You do not need the changeAmountPressed method nor the relative binding.

Can't get property from another class

I have a class RootViewController where I have a UIBarButtonItem declared. The method to display it is in another class FirstDetailViewController.
I am trying to access it in another class SecondDetailViewController, but it is always null. I tested with some other variables and they were null as well. Here's what I have:
RootViewController.h
#interface RootViewController : UITableViewController <UISplitViewControllerDelegate> {
}
#property (nonatomic, retain) UIBarButtonItem *rootPopoverButtonItem;
...
#end
RootViewController.m
#import "RootViewController.h"
#import "FirstDetailViewController.h"
#implementation RootViewController
#synthesize popoverController, splitViewController, rootPopoverButtonItem;
- (void)splitViewController:(UISplitViewController*)svc willHideViewController:(UIViewController *)aViewController withBarButtonItem:(UIBarButtonItem*)barButtonItem forPopoverController:(UIPopoverController*)pc {
NSLog(#"splitviewController will hide");
// Keep references to the popover controller and the popover button, and tell the detail view controller to show the button.
barButtonItem.title = #"Menu";
self.popoverController = pc;
self.rootPopoverButtonItem = barButtonItem;
UIViewController <SubstitutableDetailViewController> *detailViewController = [splitViewController.viewControllers objectAtIndex:1];
[detailViewController showRootPopoverButtonItem:rootPopoverButtonItem];
}
FirstDetailViewController.m
- (void)showRootPopoverButtonItem:(UIBarButtonItem *)barButtonItem {
NSLog(#"show root popover button item");
// Add the popover button to the toolbar.
NSMutableArray *itemsArray = [toolbar.items mutableCopy];
[itemsArray insertObject:barButtonItem atIndex:0];
[toolbar setItems:itemsArray animated:NO];
[itemsArray release];
}
SecondDetailViewController.h
#import "RootViewController.h"
#class RootViewController;
#interface SecondDetailViewController : UIViewController <SubstitutableDetailViewController, UIScrollViewDelegate, UITextFieldDelegate, UITextViewDelegate> {
...
}
#property (nonatomic, retain) RootViewController *root;
#end
SecondDetailViewController.m
#import "SecondDetailViewController.h"
#implementation SecondDetailViewController
#synthesize root;
...
NSLog(#"view class : %#", [root.splitViewController class]);
[detailViewController showRootPopoverButtonItem:root.rootPopoverButtonItem];
...
You probably aren't setting the "root" property of SecondDetailViewController to the instance of RootViewController that you want to access the UIBarButtonItem for. Then you're reading an uninitialized instance of RootViewController in your SecondDetailViewController code, and the only reason you don't get an error is that Objective C silently ignores calls to methods on nil objects (in this case the rootPopoverButtonItem getter method, which the root.rootPopoverButtonItem is shorthand for).
If your instance of "RootViewController" is called "myRootViewController", then somewhere in your code you have to do something like:
SecondDetailViewController *mySecondDetailViewController = [[SecondDetailViewController alloc] init];
mySecondDetailViewController.root = myRootViewController;
Then you'll be accessing the copy of RootViewController that has the bar button you want.

ViewController doesn't accept my delegate

I have a ViewController class with the following structure:
Header file:
#protocol GCStageViewControllerDelegate;
#interface GCStageViewController : UIViewController <UIActionSheetDelegate, UITextFieldDelegate, UIImagePickerControllerDelegate> {
id <GCStageViewControllerDelegate> delegate;
...
}
#property (nonatomic, retain) id <GCStageViewControllerDelegate> delegate;
...
#end
#protocol GCStageViewControllerDelegate
- (void)gcStageViewContollerDidFinish:(GCStageViewController *)controller withGCStageItem:(GCStageItem *)item;
#end
Implementation file:
- (void)viewDidLoad {
...
stageInputTextField.delegate = self; // works
...
}
- (void)takePicture {
UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
[imagePicker setSourceType:UIImagePickerControllerSourceTypeCamera];
} else {
[imagePicker setSourceType:UIImagePickerControllerSourceTypePhotoLibrary];
}
imagePicker.delegate = self; // here I get the error
[self presentModalViewController:imagePicker animated:YES];
}
If I set the delegate in the takePicture method I get the following warning:
Assigning to 'id<UINavigationControllerDelegate,UIImagePickerControllerDelegate>' from incompatible type 'GCStageViewController *'
Any ideas what's wrong?
Apparently UIImagePicker needs you to also implement the UINavigationControllerDelegate protocol. See the documentation, where it clearly shows that the delegate should conform to both. :-)
Luckily, all of the methods in that protocol are optional, so all you need to do it pretend:
// change this
#interface GCStageViewController : UIViewController <UIActionSheetDelegate, UITextFieldDelegate, UIImagePickerControllerDelegate> {
// to this
#interface GCStageViewController : UIViewController <UIActionSheetDelegate, UITextFieldDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate> {
The UIImagePickerControllerDelegate actually requires conformation to two protocols: UIImagePickerControllerDelegate and UINavigationControllerDelegate. Conform to UINavigationControllerDelegate in the header and the error will disappear.
#interface GCStageViewController : UIViewController <UIActionSheetDelegate, UITextFieldDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate>

Method implemented from delegate not triggered

I have a UIViewController which is embedded in a navigation controller and presented modally:
//UIViewController
AuthenticationController *auth = [[AuthenticationController alloc] init];
//UINavigationController
AuthRootController *navController = [[AuthRootController alloc]
initWithRootViewController:auth];
navController.navigationBar.topItem.title = #"Anmelden";
navController.delegate = self;
[self presentModalViewController:navController animated:YES];
RELEASE_SAFELY(navController);
However there is something wrong with the delegate I created within the AuthRootController class:
#protocol AuthRootControllerDelegate
#required
-(void)authRootControllerDidEnd:(UINavigationController *)sender;
#end
#interface AuthRootController : UINavigationController {
id<AuthRootControllerDelegate> delegate;
}
#property (nonatomic, assign) IBOutlet id delegate;
#end
And the implementation:
#implementation AuthRootController
#synthesize delegate;
-(void)userDidCancelController:(UINavigationController *)sender{
if (self.delegate && [self.delegate conformsToProtocol:#protocol(AuthRootControllerDelegate)]) {
[self.delegate authRootControllerDidEnd:sender];
}
}
#end
When I use the method
-(void)authRootControllerDidEnd:(UINavigationController *)sender
it is not triggered. Any ideas?
Have you declared that your delegate conforms to AuthRootControllerDelegate? The conformsToProtocol test looks at whether the delegate declares conformance, it doesn't do any sort of method-by-method check. So even if you've implemented authRootControllerDidEnd: on your delegate, conformsToProtocol can still return NO.
In your interface you aren't declaring it as implementing the delegate protocol, you need to modify your interface declaration like this:
#interface AuthRootController : UINavigationController<AuthRootControllerDelegate> {