views:

111

answers:

5

What is the easiest way to pass information between UIViews? If i have 2 standalone views that push and pop from the navigation bar and I want from one UIview to do some logic when a button pressed on the second UIview??

Is the right way is to use delegates? or is there a simple way with the interface builder? I know that I cannot drag button to action that is not on the same view in IB

thanks

A: 

The easiest way is to use delegate, init the secondViewController with:

UIViewController *secondVC = [[UIViewController alloc] initWithDelegate:firstVC callback:callback];

[firstVC.navigationController pushViewController:secondVC animated:YES];
vodkhang
so from this code how can i call to function from the firstVC?how i connect buttons from the secondVC to the firstVCdo i need to implement the protocols like regular delegate?
Amir
You can do protocol. Or in the method: buttonClicked:, you call, [self.delegate performSelector:@selector(callback)]
vodkhang
Massive overkill. Why not just set a property of the second view controller?
jlehr
Because, here is what he asked for: "If i have 2 standalone views that push and pop from the navigation bar and I want from one UIview to do some logic when a button pressed on the second UIview??"
vodkhang
I think generally, if you want to do a lot of logic, pass the viewController itself and a selector you want to be callbacked when it finishes is acceptable.
vodkhang
A: 

Here's the easiest thing. Ready to have your mind blown with how simple this can really be?

The following code demonstrates how to share a data object between two view controllers:

VCOne.h

@interface VCOne : UIViewController {
    NSString *mystring;
}

@property (nonatomic, retain) NSString *mystring;

@end

VCOne.m

@implementation VCOne

@syntheize mystring;

-(void)viewDidLoad {
    //you probably wouldn't do it here, but just so it has a 
    //place to live, I'm doing it in viewDidLoad...
    VCTwo *two = [[VCTwo alloc] initWithNibName:@"VCTwoView" bundle:nil];
    two.mystring = self.mystring;
    [self.navigationController pushViewController:two animated:YES];
    [two release];
}
-(void)dealloc {
    [mystring release];
    [super dealloc];
}

VCTwo.h:

@interface VCTwo : UIViewController {
    NSString *mystring;
}

//nb the "assign" in the following line where you're probably
//used to seeing "retain"!!
@property (nonatomic, assign) NSString *mystring;

@end

VCTwo.m:

@implementation VCTwo

@synthesize mystring;

-(void)viewDidLoad {
    self.mystring = @"I set this string inside VCTwo!";
}    

-(void)dealloc {
    [super dealloc];
}

Okay, so! One view controller has a NSString called *mystring and declares it as a @property with the retain setter semantics. The second one has a NSString called *mystring and declares it as a @property with the assign setter semantic (and, importantly, DOESN'T release it in -(void)dealloc. This is memory-safe, although it does depend on the previous VC not releasing the object away from the current one!).

Then when the first VC instantiates the second one, it assigns its mystring field to the new VC's field. The new VC accepts that object and assigns it on its own @property. Now anything you do to that variable in VCTwo is ALSO happening on the value that's referred to in VCOne. They're literally sharing that pointer now. Both view controllers have a handle on the same piece of memory.

Why not use retain inside VCTwo? Because the setter method that's synthesized when you say retain clears and resets the variable when it is set. They become separate objects, and they're not actually synced up. You can pass values between view controllers that way, but not references.

If you find yourself having issues with the object going away because it's getting released upstream (as a result of memory warnings, perhaps), you can always explicitly call [mystring retain] and keep it around. Just be sure you DO release it in -(void)dealloc if you do that.

Dan Ray
Although everything else about this example and explanation is great, use of the assign semantic here is very wrong, and a potential crasher bug. Also, Apple's docs suggest using copy instead of retain for NSString (in case the instance passed in is mutable.)
jlehr
It's a potential crasher bug IF the underlying object gets released underneath you, which I address in the last paragraph. And using the "copy" semantic destroys the whole point of it, which is to share a pointer to the same object between two view controllers.
Dan Ray
A: 

This is exactly what the responder chain is meant for. Every UIView and UIViewController on the iPhone is a subclass of UIResponder. Additionally UIViews and UIViewControllers are both automatically added to the responder chain for you.

The responder chain is a way to send messages between responders. You can send messages to objects directly or you can pass them down the responder chain. This functionality is automatically implemented on UIButton's.

If you have a reference to view2 you can add view2 as a target of a button like so:

[button addTarget:view2 action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];

If you do not have a reference you can add a target to a button like so:

[button addTarget:nil action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];

Setting the target to nil means the action will be past down the responder chain until a responder is found that responds to the buttonClicked: selector.

For more information take a look at UIControl documentation and UIResponder documentation.

klaaspieter
+1  A: 

You use the NSNotificationCenter to send messages to any other class that has registered an interested in that topic. Really easy :)

Notification Programming Guide

willcodejavaforfood
A: 

I use NSUserDefaults. You can save the specified Item in the App, launch it in the next view, and be able to launch it again if you go back to the first view! For example...


//Saving the file - use in the first view controller
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:somedataobject];
[prefs setObject:colordata forKey:@"DataKey"]

//Recalling the file - use in 2nd view
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
NSData *objectData = [prefs objectForKey:@"DataKey"];
Object *someobject = [NSKeyedUnarchiver unarchiveObjectWithData:objectData];

Now this is assuming that the data is NOT an NSInteger, an NSString, or a boolean value. In that case you would use: [prefs setInteger: forKey:]; [prefs setBool: forKey:]; for the declaration, and [prefs IntegerforKey:] [prefs BoolforKey:] for recalling.

The above syntax also works for doubles and floats.

Flafla2