Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing method of viewController from another class

I've been trying to use a UIButton action to call a method in a different class (AppViewController). I first tried creating an instance of the view controller in the UIButton's calling class (caller.m) and then calling the method, but that kept resulting in EXC_BAD_ACCESS.

I'm realizing I need to point to the same instance of the view controller and am now trying to make sure the view controller instance is properly declared in caller.m.

I have a declaration of AppViewController *viewController in the AppDelegate, so my thought is to refer to that same instance from caller.m.

#import "caller.h"
#import "AppDelegate.h"

@implementation caller

- (id)initWithFrame:(CGRect)frame {
...
[btnSplash addTarget:viewController action:@selector(loadSplashView) forControlEvents:UIControlEventTouchUpInside];
....
}

However, viewController still shows up as undeclared. I tried a few other things, but know I'm probably missing something basic.

::::UPDATE::::

Okay, so it turns out I needed to create the following so the target "viewController" was actually declared and pointing to the correct instance:

AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
AppViewController* viewController = appDelegate.viewController;

The method in the view controller class is now being properly called.

For a more clearly explained and more general version of this question, go here: Objective-c basics: Object declared in MyAppDelegate not accessible in another class

like image 459
Old McStopher Avatar asked Jan 20 '23 01:01

Old McStopher


2 Answers

There are multiple ways for objects to initiate actions, communicate with other objects and/or observe changes they are interested in including:

  1. UIControl target/action bindings
  2. Protocols
  3. Key/Value Observing (KVO)
  4. Notifications

I don't think notifications are what you want in this case. Notifications are most appropriate when the object posting the notification does not care what object(s) are observing the notification and there can be one or more observers. In the case of a button press you would typically only want a specific object to handle the action.

I would recommend using a protocol. You'll see lots of protocols in use in the iOS frameworks, basically any class that has a delegate property usually defines a protocol that delegate objects need to conform to. The protocol is a contract between the two objects such that the object defining the protocol knows that it can communicate with the object conforming to the protocol with out any other assumptions as to its class or purpose.

Here's an example implementation. Apologies if any typos/omissions.

In caller.h (I assumed caller is a UIViewController):

@class Caller

@protocol CallerDelegate
    - (void)userDidSplashFromCaller:(Caller *)caller;
@end

@interface Caller : UIViewController
    id <CallerDelegate>    delegate;
@end

@property (nonatomic, assign)    id <CallerDelegate>    delegate;

@end

In caller.m:

@implementation Caller

@synthesize delegate;

- (void)viewDidLoad {
    // whatever you need
    // you can also define this in IB
    [btnSplash addTarget:self forAction:@selector(userTouchedSplashButton)];
}

- (void)dealloc {
    self.delegate = nil;
    [super dealloc];
}

- (void)userTouchedSplashButton {
    if (delegate && [delegate respondsToSelector:@selector(userDidSplashFromCaller:)]) {
        [delegate userDidSplashFromCaller:self];
    }
}

in otherViewController.m:

// this assumes caller is pushed onto a navigationController
- (void)presentCaller {
    Caller *caller = [[Caller alloc] init];
    caller.delegate = self;
    [self.navigationController pushViewController:caller animated:YES];
    [caller release];
}

// protocol message from Caller instance
- (void)userDidSplashFromCaller:(Caller *)caller {
    NSLog(@"otherVC:userDidSplashFromCaller:%@", caller);
}

[EDIT: CLARIFICATIONS]

I realized after looking at your question and code again that I made some assumptions that may not be true in your code. You most likely should still use a protocol but the exact way to integrate my example depends on your app. I don't know what class Caller is in your app but whatever it is, it is dealing with UIButtons so it is most likely a view controller or a view.

Your comment about not having the correct instance of your appViewController makes me wonder if you understand the difference between classes and instances of a class. If my answer doesn't help you, please post some more code showing how you create and present your view controller as well as how you are configuring the button and I can try to clarify my answer.

like image 105
XJones Avatar answered Jan 30 '23 06:01

XJones


You should post a NSNotification when clicking the button that will be caught and executed in the AppViewController.

So this should be: In the sender class:

[btnSplash addTarget:self 
              action:@selector(loadSplashView) 
    forControlEvents:UIControlEventTouchUpInside]; 

-(void)loadSplashView:(id)sender 
{ 
[[NSNotificationCenter defaultCenter] postNotificationName:@"notif_name" object:some_sender_object];
}

In the target class: Register to get the notification at view's load:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(some_function:) name:@"notif_name" object:nil];

Define the action to take in this class:

-(void) some_function:(NSNotification *)notif {
   //do something
   // to access the object do: [notif object]
}
like image 25
Stefan Ticu Avatar answered Jan 30 '23 05:01

Stefan Ticu