How to call a method from another class

Hello everyone.


I have 2 classes.

  1. ViewController1
  2. ViewController2


In ViewController1 I have a method:

- (void)hideBirthFrameOutlet2
  {
    self.BirthFrameOutlet2.hidden = YES;
  }




I want to call my hideBirthFrameOutlet2 method from outside my ViewController1 class. I want to call it from within ViewController2 from within this method:

- (IBAction)goBackToPage1:(id)sender
  {
    // put the code right here 
    [self.navigationController popViewControllerAnimated:YES];
  }




I have found lots of examples in stack overflow on how to do this and for the last 3 days I have been attempting call my hideBirthFrameOutlet2 method from outside my ViewController1 class. It should be simple.


I am aware that I should #import ViewController1.h into ViewController2.h right under the other imports and I have but I just can't get it to work. I know what I am attempting is a very doable.


Can someone here help me out on this?


JR

Did you add the -hideBirthFrameOutlet2 method to ViewController1's header file? It should have a line like this inside the @interface:

- (void)hideBirthFrameOutlet2;


Remember that the header file alone is what's visible to the rest of your code, so everything that's not private needs to be declared there.

It is unclear what you really want to do. If what you want to do is call a method from another class then you need 2 things

1) as noted above, you need a reference to the method in the .h file for that class

2) you need a reference to the .h file for that calss - actually, you most likely need a reference to the .h file of the instantiated class - that is a reference to the actual object that is represented by that class. If that is what you want then you will need to pass to viewController2 a reference to viewController1 from the class that created the two viewControllers - most likely your app delegate.


However, it seems like you may want something different - it seems like you are poping from ViewController2 back to viewController1 and when you get back to viewController1 you want to execute a particular method. You can do that from the viewWillAppear method in viewController1. You may want to set a model variable from within viewController2 and check that model variable in the viewWillAppear in viewController1 to see if you should execute the method in viewController1.

Pardon my observation, but what you said is really, really confusing. I've been writing Cocoa code for years, but even I am not sure exactly what you mean.

I mean to be complete. I am sorry if that is confusing. Unfortunately,I do not know which part of my comment was confusing so I can't really respond.

Hello bob133 and PBK.


First to you bob. Yes I did put that in the .h file.


I almost have it. I will post the code in detail below. This will hopeully make things clearer to the both of you.


// XTBMainViewController1.h  --> This is the target class that has the method I want called
//                              from the XTBMainViewController2 class

#import <MediaPlayer/MediaPlayer.h>
/
#import <AudioToolbox/AudioToolbox.h>
#import <AVFoundation/AVFoundation.h>
#import <UIKit/UIKit.h>

@interface XTBMainViewController1: UIViewController <NSObject>

...

- (void)hideBirthFrameOutlet2; // here is the method in question declared so it is public
@end


// XTBMainViewController1.m


#import "XTBMainViewController1.h"


@interface XTBMainViewController1 ()
@end
@implementation XTBMainViewController1
/
- (void)viewDidLoad
  {
    [super viewDidLoad];
     
    ...

- (void)hideBirthFrameOutlet2 // this is the implementation of the method in question
  {
    self.BirthFrameOutlet2.hidden = YES;
  }




- (IBAction)goForwardToPage2:(id)sender
  {
    [self showBirthFrameOutlet2];
    [moviePlayerController stop];
  }


- (IBAction)goBackToCoverPage:(id)sender
  {
    [self.navigationController popViewControllerAnimated:YES];
    [self showBirthFrameOutlet1];
    [moviePlayerController stop];
  }

@end


-----------------------------------------------------------------------------------------------------------------------------------------


// XTBMainViewController2.h  --> This is the class where I will call the (void)hideBirthFrameOutlet2 method 
//                               found in the XTBMainViewController1 class

#import <AudioToolbox/AudioToolbox.h>
#import <AVFoundation/AVFoundation.h>
#import <UIKit/UIKit.h>

#import "XTBMainViewController1.h"// I'm supposed to import XTBMainViewController1.h file into XTBMainViewController2.h file 


@interface XTBMainViewController2 : UIViewController <NSObject>
  {
    SystemSoundID PageTurnSoundID; / when you turn the page this object will  be called */
    XTBMainViewController2 *callHideBirthFrameOutlet2Method;
  }
@property (nonatomic, retain) XTBMainViewController2 *callHideBirthFrameOutlet2Method;
- (IBAction)goBackToPage1:(id)sender;
@end


// XTBMainViewController2.m


#import "XTBMainViewController2.h"
@interface XTBMainViewController2 ()
@end
@implementation XTBMainViewController2
@synthesize callHideBirthFrameOutlet2Method;
/
- (void)viewDidLoad
  {
    [super viewDidLoad];
  }

...

// it is from within this method I want to call the (void)hideBirthFrameOutlet2 method found in the
// XTBMainViewController1 class
- (IBAction)goBackToPage1:(id)sender
  {
    self.callHideBirthFrameOutlet2Method = [[XTBMainViewController1 alloc] init]; //yellow error Incompatible pointer types assigning to 'BMainViewController2 *'from 'XTBMainViewController1 *'
    [self.callHideBirthFrameOutlet2Method hideBirthFrameOutlet2];///red error No visible @interface for 'XTBMainViewController2'declares the selector 'hideBirthFrameOutlet2'   
    [self.navigationController popViewControllerAnimated:YES];
  }

...

@end


There. The green comments explain what is going on. I replaced code that has nothing to do with the problem at hand with 3 dots '. . .'.


What I am trying to do is simply call a method in one class from within another class. I am sure this is a very common thing because I see so many posts on Stack Overflow and other web sites about this.


JR

You're doing a number of things oddly. First, you should rename your callHideBirthFrameOutlet2Method property to something that makes more sense for your code—something like viewController1. Second, I notice that the property is declared as a ViewController2, but you're storing a ViewController1 in it. The compiler doesn't understand why you're placing a ViewController1 in it, and it gets even more confused when you try to call a ViewController1 method on it Change the property declaration in the header file so that it's the proper class and it should compile correctly.


Even though it might compile, that code would not produce the result you expect. Why are you creating a brand-new view controller that will never be used in your interface? Your code should probably be written like this instead:

- (IBAction)goBackToPage1:(id)sender {
     NSArray<__kindof UIViewController *> *viewControllers = self.navigationController.viewControllers;
     XTBMainViewController1 *previousController = viewControllers[viewControllers.count - 2];
     [previousController hideBirthFrameOutlet2];
     [self.navigationController popViewControllerAnimated:YES];
}


Your existing code was calling that hide method on a brand-new view controller, which is never used in your UI. What you should do instead is retrieve the existing view controller from the stack and call the hide method on that one. If this is all you're doing, you shouldn't need to have a property for the view controller.

keep in mind that a .h file and its .m file can be used to create many many objects and, in this case, view controllers. What you want to do is interact with the particular view controller that was already created by your app delegate or by your storyboard. You most likely do not want to import the .h file as bob suggested. You need to find a reference to, what is called, the instantiated object. That, or handle it when you return to the viewController in question. I recommend you try to understand and respond to my first post. On rereading it I found it quite correct.

Thank you bob133 for helping me work through this can of worms.


I have a convention that I name something according to what it does. The purpose of this property is to call the hideBirthFrameOutlet2 method in XTBMainViewController1.m from XTBMainViewController2. Therefore I feel callHideBirthFrameOutlet2Method is a good one (a bit long, I admit but a good one).


Now your second observation about using the name XTBMainViewController2 when the method I want called is in XTBMainViewController1 is absolutely spot ON! I changed the 2 to a 1 and it compiles and the 2 error messages are gone! Unfortunately, you were also correct with the "Yes, it compiles now but that won't do YOU any good" prediction. It doesn't do me any good. The BirthFrameOutlet2 is still visible covering the animation (which starts to play again, under BirthFrameOutlet2) when I leave page 2 to go back to Page1. Do you have to be right all the time?


Your following question has me totally mystified. Quote: "Why are you creating a brand-new view controller that will never be used in your interface?"


I don't see how I am creating a new view controller. To my understanding, I have created a view controller for each page of the comic book. Each view controller is made up of a view controller in the storyboard and a corresponding view controller class. When I call the - (IBAction)goBackToPage1) method, I thought I was simply doing that - going back to Page1 with its one storyboard viewcontroller and its accompanying viewController class.


Are you saying that by coding:

    self.callHideBirthFrameOutlet2Method = [[XTBMainViewController1 alloc] init];

I'm creating a whole new view controller!? I know I am revealing the profound depths of my noobie-ism here but I simply don't understand.


We'll get back to that one later. I see you suggest some code that I should key in instead. To me, it looks just like Greek but you seem to know what you're talking about so I'll try it out...


OMG!! It works....almost! This is what happens. I turn to page one from the Cover Page and the animation kicks in as it should. I leave Page 1 to Page 2 in the middle of the animation and then I go back. Your code hides BirthFrameOutlet2! Then it comes back again.😮 Sometimes, it comes back in 3 seconds; other tries, it comes back in 6 seconds. I think that when I go back to Page 1, the code I have written for that page starts to work again unhiding BirthFrameOutlet2. I think I can work this out myself. I think you solved it bob!


If you just explain to me how I am creating a new view controller that will turn this into a real learning moment for me. And... ( I know I'm asking a lot here)


if you could explain the code (you know - the Greek code) you gave me solving the problem, that would be really helpful so I can place that into the comments within my code.


JR

Hello PBK.


Yes, I will go over you first post. As you can see bob133 has solved my problem and my BirthFrameOutlet2 is now hidden when I go back to Page 1. The fact that it comes back (most likely is due to the code that I have written in XTBMainViewController2.m kicking in again) is another issue, one I think I can solve it myself. (I hope!)


Maybe the explanation in your post will help me in finding the solution.


My grasp on this new skill I am developing is tenuous. I get easily confused and flustered. But I am now able to (with the help of the web site Stack Overflow and the help of people on this forum) to solve my problems. A year and a half ago, I wasn't able. So I am making progress.


Thank you for your suggestions. I appreciate them.


JR

Yes, you're creating a totally new object any time you use the initializer syntax:

[[SomeClass alloc] init]

Or a class factory method, like these:

[NSArray arrayWithObjects:...]
[UIColor colorWithCalibratedRed:...]

Now there sometimes will be some fancy caching behavior applied to classes like NSIndexSet, for example, but with view controllers, you're always getting back a brand-new object.

Oh, and you should change that habit of naming variables according to what you do with them. That's going to get really confusing and hard to maintain. What would happen if you decided to use that self.callHideBirthFrameOutlet2Method object for something other than that method? Your name would no longer match what it does, making for incredible confusion. Instead, you should name the variable according to either what it is or what it does for you. For example, previousViewController. You can use that all over your code without it looking odd or confusing you.


Of course, though, in this particular scenario you don't need the instance variable anyway, so you should probably just delete this one here and use a local variable for that previous view controller like I showed in my code above.

Hello again.


Yes, now that I have time to think about it, I am producing an entirely new object when I do that. When I use "alloc" I'm making a space in memory for that object and when I am using "init" I'm setting all the pointers to zero. Presto! New object.


I'll have to read up on the factory method part...


Now in your second post you say:

"What would happen if you decided to use that self.callHideBirthFrameOutlet2Method object for something other than that method?"

You then suggest that I replace the name I used with "prviousViewController". I admit the name is shorter, however, the object in question calls a method in the previous view contorller class (Page1) called "hideBirthFrameOutlet2" which hides an outlet that covers the animation. This outlet is in the storyboard. You suggestion doen't discribe what it does.


As I write this, I get the creepy feeling that I am missing the point of what you are saying. But it is late at night here where I am living so I'll sleep on it. Then I'll go over it again in the morning. But the nice thing is that you solved the problem for me and that makes me a happy man.


But if you could, explain the code in your solution it would be great because I /* comment */ everything in my code so I know what is going on when I revisit it a couple of months later. (A good habit, don't you agree?)


Thanks again.


JR

It's getting kind of tight here, so I'll move down to the bottom of the thread.

Sure. Here you go:

- (IBAction)goBackToPage1:(id)sender {
     //Get the current list of view controllers
     NSArray<__kindof UIViewController *> *viewControllers = self.navigationController.viewControllers;
     //Retrieve the previous view controller in the stack: remember that the last object in an array will be
     //at index count - 1, so I'll grab the second-to-last because the last one is the view controller on top.
     //Store this in a local variable because there's no need to hang on to the reference after we're done here.
     XTBMainViewController1 *previousController = viewControllers[viewControllers.count - 2];
     [previousController hideBirthFrameOutlet2];
     [self.navigationController popViewControllerAnimated:YES];
}


And I see what you mean about wanting the name of the variable to be immediately clear about what its purpose is, but almost nobody does it that way. In my opinion, you would have greater clarity by making the object's identity more obvious—after all, the method call makes it clear why it's there and what it does. A variable like "callHideBirthFrameOutlet2" could be anything—actually, the first time I read your code I thought you were trying to call the method the super-hard way, with really advanced stuff like NSInvocation (It's a class for interacting with the Objective-C dynamic runtime—something you rarely need in real code). If you had named it "previousViewController," it would have been immediately clear to me that the variable was, in fact, a view controller.

And the local variable is *previousController. Am I correct?


JR

How to call a method from another class
 
 
Q