Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling Method From Another UIViewController Has No Visible Effect

I have two classes and want to call a method from one class when a button is pressed. I declare it in my .h file as so:

-(void) imageChange;

And I created the method in my .m like this:

-(void)imageChange {
    UIImage *image = [UIImage imageNamed: img];
    [_MyImage setImage:image];
}

Finally, I tried to call the method from another class using:

- (IBAction)Done:(id)sender {

    SecondViewController *theInstance = [[SecondViewController alloc] init];
    [theInstance imageChange];
    [self dismissViewControllerAnimated:YES completion:nil];

}

However, when I press "done" in my view controller, the UIImage doesn't change. Please note: img is an NSString value.

like image 622
C-O-D-E Avatar asked Jun 19 '14 17:06

C-O-D-E


1 Answers

The question is a variation on a common one: "how to pass values between view controllers", and your code represents a common attempt at a solution. Let's start with what you did.

Your app has two view controllers with views on the view stack, and you want to communicate something between them. This line:

SecondViewController *theInstance = [[SecondViewController alloc] init];

creates a brand new instance of SecondViewController (alloc means allocate memory for a new instance of this class). This line:

[theInstance imageChange];

communicates something to it, in your case it looks like setting an image view's image. Then this line:

}

implicitly destroys that new instance, since it's never referred to again. So your code succeeds in communicating with a SecondViewController, but with the wrong instance, an instance that lives for only a few milliseconds.

Okay, what to do about it? What @rmaddy was saying is go find the existing instance of SecondViewController, and communicate with that. How to get ahold of that existing instance depends on how we got here. The dismissViewControllerAnimated in your code makes me think that this current vc was presented by an instance of SecondVC. If so,

(SecondViewController *)self.presentingViewController

points to what you need. If we were in a UINavigationController, you could dig through it's viewControllers stack, probably here:

NSArray *stack = self.navigationController.viewControllers;
SecondViewController *secondVC = stack[stack.count-2];

But, while all that might be the straightest line from A to B, it's not very good design because it makes this current view controller dependent in a brittle way on how it got presented.

Hence @CrimsonChris makes a good suggestion to consider delegation. This is most peoples' go-to pattern for a vc that needs to communicate with another. There are plenty of web and SO resources on how to do this, so I won't repeat here. Check out this, for example, or google "iOS delegation".

There are other ways, like NSNotificationCenter to broadcast to everyone that's interested whatever you want to communicate, or KVO that lets the the SecondVC observe a change in your model and react, no matter how or why that change was made.

The key concept for these latter two is that your app needs to have a model, a set of objects that describes the app state. The view controllers are not the model -- in fact they're exactly not the model. Their job is to learn about model changes and modify the views accordingly.

For your case, the code you posted shouldn't be trying to get an image set in the other view controller, it should be recording into the model whatever user action that happened that triggers the dismissal. When this current vc dismisses itself, the SecondViewController (assuming it did the present) will get a viewWillAppear. That method might be a good place to check the model condition that was set on the user action. Then SecondViewController can call imageChange on itself.

Hope that was clear enough. Good luck.

like image 173
danh Avatar answered Nov 12 '22 15:11

danh