Passing data back from child view controller - objective-c

I am a beginner, and I have a project in which I need to pass data back from a childviewcontroller. Specifically, I have a picker view in my container and I want to be able to use it to select an option (say to simplify just a color to chose from an array of ten colours). Then I would like to be able to access the chosen color in my parent view controller and do something with it. I have researched this for a couple of days now and the closest answer to what I am looking for I found in a related question on S.O. Here it is:
"Regarding passing value to the containerView controller, you can make a property in the ChildViewController of the value you want it to be passed. Then in your ParentViewController do something like the following:
self.ChildViewController.yourProperty = yourValue
The opposite can be done in 4 ways:
You can make a delegate protocol to communicate the data between your controllers.
You can post a notification in your ChildViewController and add the parent controller as an observer.
You can use KVO.
And the easiest way out, you can make a property in your parentviewController and access it like the following:"
((YourParentViewControllerClassType *)self.parentViewController).yourParentProperty = TheValueYouWant;
Now, I would like to try the fourth option first as delegation, kvo and so on are options I have read about but not ready to tackle yet. What I would need help with is the last option.
Say I had a property in my child view controller where I store the chosen color. Something like:
#interface ViewController ()
#property NSString *ChosenColorInChildVC;
#end
And then, later on:
self.ChosenColorInChildVC = [self pickerView:myPickerView titleForRow:[myPickerView selectedRowInComponent:1] forComponent:1]];
how would I pass that value using the proposed:
((YourParentViewControllerClassType *)self.parentViewController).yourParentProperty = TheValueYouWant;
Can someone dumb it down a little further for me? Thanks

I'm going to explain you with an example how delegation works.
Edit your ChildViewController.h like this:
#protocol ChildViewControllerDelegate;
#interface ChildViewController : UIViewController
#property (weak)id <ChildViewControllerDelegate> delegate;
#end
#protocol ChildViewControllerDelegate <NSObject >
- (void) passValue:(UIColor *) theValue;
#end
On your ChildViewController.m when you want to pass something back to the ParentViewController , do like this:
- (void) myMethod
{
[delegate passValue:[UIColor redColor]]; // you pass the value here
}
On your ParentViewController.h
#import "ChildViewController.h"
#interface ParentViewController : UIViewController <ChildViewControllerDelegate > // you adopt the protocol
{
}
On your ParentViewController.m:
- (void) passValue:(UIColor *) theValue
{
//here you receive the color passed from ChildViewController
}
Now be careful. Everything will work only if you set the delegate.
So when you declare ChildViewController inside your ParentViewController class, do like this:
ChildViewController * controller = [[ChildViewController alloc]initWithiNibName:#"ChildViewController"];
controller.delegate = self; //without this line the code won't work!

#metronic is right; use delegation.
Also typically you will include the sender in your delegate methods:
-(void) childViewController:(ChildViewController*)viewController passValue:(UIColor*) theValue

Noted: this is very important.
Write protocol declaration code above the #import lines e.g.
#protocol -----
#end
import ----
#interface classname ---

Related

Can anyone please help me out? iOS How Datasource methods are implemented? and How do they work?

I am trying to implements my own Datasource methods.Not getting how can we implements.
Datasources and delegates are both implemented using protocols. The difference between the two is that the delegate controls user interaction and the datasource controls data.
This means that your typical delegate method will be called from something like a button click and your datasource method will be called during initialization or refresh of the control. It also means that delegates are more likely to have void methods and data source methods will most likely define a return type.
If you take an example that has a view controller and your custom control, then you would do something like this to setup your datasource protocol.
CustomControl.h:
#protocol CustomControlDatasource <NSObject>
- (NSInteger)NumberOfThingsInControl;
#end
#interface CustomControl : NSObject
#property (nonatomic, weak) id <CustomControlDatasource> delegate;
#end
You could call datasource methods from your init or refresh method and use the result to configure the control however you'd like (don't forget to synthesize the delegate property).
CustomControl.m:
#synthesize datasource = _datasource;
...
-(void)refresh {
NSInteger numOfThings = [self.datasource NumberOfThings];
// Use the result to update the control
...
}
The class that implements the datasource needs to conform to it and implement the datasource method.
MyViewController.h:
#interface MyViewController : UIViewController <CustomControlDelegate> {
CustomControl *coolControl;
}
#end
MyViewController.h:
- (void)viewDidLoad {
[super viewDidLoad];
coolControl = [[CustomControl alloc] init];
[coolControl setDatasource:self];
[coolControl refresh];
}
- (NSInteger)NumberOfThingsInControl {
return 3;
}
I hope this clears things up a bit. protocols are an awesome way to have objects communicate.
You can read more in the documentation:
Working with Protocols
Delegates and Data Sources

Objective-C iOS 6 delegate NSString query

I'm trying to use a delegate to pass a value from one VC to another. I think I'm am misunderstanding the way it is supposed to work.
In my main ViewController.h I have this:
#protocol defaultLocationChoice <NSObject>
- (NSString *) locChoice;
#end
In both my PreferencesViewController.h and ChooseServerViewController.h I have defaultLocationChoice declared in the #interface section and the property assinged like so:
#property (nonatomic, assign) id <defaultLocationChoice> locationDelegate;
Both are synthesized also.
When the user segues from PreferencesViewController to ChooseServerViewController the prepare for segue code is:
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"toServerChoice"]) {
ChooseServerViewController *viewController = (ChooseServerViewController *)segue.destinationViewController;
viewController.locationDelegate = self;
}
}
When a cell choice is made in ChooseServerViewController I call:
[self locChoice];
Which is:
- (NSString *) locChoice {
NSLog(#"Cell Vale Loc choice %#",cellValue);
return cellValue;
}
The NSLog verifies the correct value is returned.
Now, as I think I understand it, the value of LocChoice in the delegate is now the value returned, no?
When the user goes back (NavController) the PreferencesViewController has:
-(void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
defaultLocation = [locationDelegate locChoice];
[self.tableView reloadData];
}
I was expecting the value of defaultLocation to now equal the value passed to locChoice. However when the table reloads the cell in question is still blank, implying what I exepct to happen isn't happening.
Any ideas?
If I followed your code properly, you do not need to adopt the mentioned protocol in your ChooseServerViewController, only PreferencesViewController.
The reasoning is you want to send data back to the previous view controller. Try:
#protocol defaultLocationChoice <NSObject>
- (void) locChoice:(NSString*)choice;
#end
Have your PreferencesViewController implement that method so it receives the selection. You will have to store that in an appropriate instance variable.
// in PreferencesViewController.m
-(void)locChoice:(NSString*)choice {
self.choice = choice; // this just my example
}
When the choice is made (in ChooseServerViewController) to send the choice back, call:
// this is in 'ChooseServerViewController.m' some where appropriate
[self.delegate locChoice:cellValue];
Your implementation is simply doing nothing with cell value (not even storing it, just logging it). When you return to PreferencesViewController, you will now have the selected value and that view controller can what it wants with it.
Protocols are somewhat analgous to interfaces in Java or C#, but more flexible.
Some more concepts about delegation.
Working with Protocols.
UPDATE:
The declaration for ChooseServerViewController should look like:
#import "FileWithProtocolDecalration.h"
#interface ChooseServerViewController
.
.
.
#property ( nonatomic,assign) id<defaultLocationChoice> delegate;
.
.
.
#end
I think you do have some misunderstanding there
protocol and delegates are something that is passed around. in other words somebody need to be receiver and somebody need to be the sender. in your case.
update your protocol to this
#protocol defaultLocationChoice <NSObject>
- (void)locChoice:(NSString *)updateString; // the method from delegate and implementer must be exact
#end
and set implement the protocol to ViewController as a receiver
#interface VCReceiver : UIViewController <defaultLocationChoice>
then later in VCReceiver
- (void)viewDidLoad {
ChooseServerViewController *vcSender = [[ChooseServerViewController alloc] init];
[vcSender setLocationDelegate:self]; // this is like saying. i have implemented method from protocol in this file (self)
}
- (void)locChoice:(NSString *)updateString {
// update the VCReceiver here
// or access vcSender value
// or use the updateString value
}
then in ChooseServerViewController locChoice: method (the one from your example) replace with this one and call [self updateChoice] instead:
- (void)updateChoice {
if ([self.locationDelegate respondsToSelector:#selector(locChoice:)]) {
[self.locationDelegate locChoice:aStringToUpdate]; // this will call VCReceiver locChoice
}
it does not have to return anything because it is actually calling the VCReceiver method to tell it that ChooseServerViewController got the value ready to be read.

What's the best way to have all different view classes to implement the same method in Objective-C

I am creating one of my first Objective-C apps. I have a bunch of UIViews and Other views such as MapView and so forth.
In my Model I have a method called register that all views, well, register with. They are all put in a NSMutableArray in the model. Any time the model has updated data I run a for loop over the array of "views" and I would like all views no matter there type to implement a method called "update" that can be called from the model on each view.
Being that a view could be a UIView or of any other type I'm not really sure what the Objective-c way is of doing this is.
I'd really like to be able to do something like the following but I know this is not correct.
for(NSInteger i=0; i<[_views count]; i++)
{
NSObject *view = [_views objectAtIndex:i];
[view update:data];
}
Define a protocol:
#protocol IUpdateable <NSObject>
#required
-(void)update:(id)data;
#end
Then in your view class implement the protocol...
#interface SomeView : UIView <IUpdateable>
#end;
#implementation SomeView
-(void)update:(id)data
{
//Do something with data
}
#end;
Then you can call your methods in one fell swoop assuming your _views array contains a collection of IUpdateable views...
[_views makeObjectsPerformSelector:#selector(update:) withObject:someData];
Reference: NSArray, Working with Protocols
You are trying to do the same thing the Cocoa delegates do. Define a formal protocol with the update method, and make all your UIView classes (or any other classes that you want to be registerable with the model) adopt that protocol.
Define the protocol:
#protocol Updateable <NSObject>
#required
- (void) update: (id)data;
#end
Adopt the protocol:
#interface MyView : UIView <Updateable>
- (void) update: (id)data;
...
#end;

Calling a method from another 'm' file

The bulk of the code for my app is in a 'm' file called MyViewController. The app implements a custom UIView which contains a UIWebView object. The code for the UIView and UIWebView is kept in a separate 'm' file called CustomUIView.
I have managed to override clicks on URL hyperlinks in the UIWebView object using a delegate. However, I would like to have these clicks launch a method that is stored in my main app code. This method is called "popupView", and takes a single argument, "inputArgument". The inputArgument is the text of the URL the user clicks on. In fact, this method is the very same one that causes my custom UIView to launch.
Anyway, what I'd like to do is have my overridden URL clicks cause the popupView method to launch, thus causing another UIView to open on top of the one that was clicked on.
The problem is that the 'm' file where the URL clicks are detected can't see the 'popupView' method as it is included in the MyViewController 'm' file. How do I call the popupView method from another 'm' file?
Directly
Declare MyViewController's method -popupView: in MyViewController.h.
#import MyViewController.h in CustomUIView.m.
Give CustomUIView a reference to the [one] instance of MyViewController, for example by way of an #property declared in CustomUIView.h.
For (1), the #interface of MyViewController (in MyViewController.h) should look a bit like this
#interface MyViewController : UIViewController
{
//....
}
- (void)popupView:(NSString *)urlText;
//....
#end
For (2), UIViewController.m should have the following somewhere near the top
#import "CustomUIView.h"
#import "MyViewController.h"
For (3), the #interface in CustomUIView.h should look something like
#interface CustomUIView : UIView
{
//....
}
#property (nonatomic, weak) MyViewController *viewController;
#end
This property will need to be set some time after the instance of CustomUIView owned by MyViewController is created. If your CustomUIView is in MyViewController.xib, you can set this property on it by adding the keyword IBOutlet to the property's declaration like this
#property (nonatomic, weak) IBOutlet MyViewController *viewController;
and pointing this property to "File's Owner" in the XIB. If instead, you create the CustomUIView programmatically, you can set this property on it as soon as you have initialized it.
Delegate
This, however, is far from being a best practice. It would be much better to make use of the delegate pattern. To do this, you'll need to
Define a delegate protocol.
Add a "delegate" #property to CustomUIView.
Call the delegate methods on the delegate object at the appropriate times.
Implement the protocol in MyViewController.
Set the "delegate" #property of the instance of CustomUIView owned by the MyViewController instance to be the MyViewController instance.
Let's call our delegate protocol something imaginative like CustomUIViewDelegate. For (1), we'll declare it at the top of CustomUIView.h as follows:
#class CustomUIView;
#protocol CustomUIViewDelegate <NSObject>
- (void)customUIView:(CustomUIView *)customView didSelectURLText:(NSString *)urlText;
#end
Notice that we've had to forward declare our class CustomUIView so that the compiler is able to make sense of the type of the first argument in the protocol method customUIView:didSelectURLText:.
For (2), we'll do something quite similar to (3) above: Your CustomUIView #interface will look something like
#interface CustomUIView : UIView
{
//....
}
#property (nonatomic, weak) id<CustomUIViewDelegate> *delegate;
#end
Again, if we're going to set this property in Interface Builder, we'll need to use the IBOutlet keyword to announce it to IB:
#property (nonatomic, weak) IBOutlet id<CustomUIViewDelegate> *delegate;
For (3), we need to call the delegate method customUIView:didSelectURLText: on our delegate object self.delegate at the appropriate time.
In your question, you wrote
I have managed to override clicks on URL hyperlinks in the UIWebView object using a delegate.
So, let's say that CustomUIView has an instance method
- (void)didSelectURL:(NSURL *)url
{
//....
}
which you call when the user selects a link in the UIWebView. The CustomUIView's delegate needs to be informed of this:
- (void)didSelectURL:(NSURL *)url
{
//...
if ([self.delegate respondsToSelector:#selector(customUIView:didSelectURLText:)]) {
{
[self.delegate customUIView:self didSelectURLText:url.absoluteString];
}
}
Notice that we check first whether the CustomUIView instance's delegate object implements the selector of interest (customUIView:didSelectURLText:) by calling respondsToSelector: on it.
For (4), we'll need first to add <CustomUIViewDelegate> to MyViewController's #interface declaration and be sure to #import CustomUIView.h into the file where we use the symbol CustomUIViewDelegate. Our MyViewController's #interface will look something like this:
#import "CustomUIView.h"
#interface MyViewController : UIViewController <CustomUIViewDelegate>
{
//....
}
//....
#end
More importantly, we need to implement the CustomUIViewDelegate protocol in MyViewController's #implementation; so far we've only declared that MyViewController adopts it.
To do this, since our protocol consists of only one method, we'll need only to add our own implementation of -customUIView:didSelectURLText:. Our MyViewController's #implementation will look something like this:
#import "MyViewController.h"
#implementation MyViewController
//....
- (void)popupView:(NSString *)urlText
{
//....
}
#pragma mark - CustomUIViewDelegate
- (void)customUIView:(CustomUIView *)customView didSelectURLText:(NSString *)urlText
{
[self popupView:urlText];
}
//....
#end
Finally, for (5), we'll need to set the delegate property of the instance of CustomUIView owned by the MyViewController instance. I don't know enough about MyViewController's relationship with its CustomUIView instance to do describe how to do this definitively, but I'll provide an example: I'll assume that you programmatically, in -[MyViewController loadView] add the CustomUIView as a subview of MyViewController's view. So your implementation of -loadView looks a bit like this:
- (void)loadView
{
[super loadView];
//....
CustomUIView *customView = //....
//....
[self.view addSubview:customView];
//....
}
All that remains to do at this point is to set the delegate #property of the local variable customView to self:
customView.delegate = self;
Edit: Updated (5) in light of new information about the relationship between CustomUIView and MyViewController.
In your comment, you write that your CustomUIView is added as a subview of cvc.view where cvc is an instance of CustomUIViewController in CustomUIView's method -[CustomUIView show]. On account of this, you note that writing customView.delegate = self; is the same as writing self.delegate = self, which is clearly not what you want to do.
You want to set the CustomUIView's delegate property to be the instance of MyViewController. Consequently, your method -[CustomUIView show] should look something like
- (void)show
{
//....
[cvc.view addSubview:self];
self.delegate = mvc;
}
where mvc is the instance of MyViewController.
Well, since you are writing the CustomUIView, why not implement a constructor like initWithPopupDelegate:(MyViewController *)delegate and keep a reference to the MyViewController instance that way in an instance variable, then call the method on that.
(Add #class MyViewController; at the top CustomUIView.h, and #import "MyViewController.h" at the top of CustomUIView.m so the compiler knows the class you are using.)
Alternatively, if there is ever only one MyViewController instance, you can define a class method for MyViewController, e.g., + (MyViewController *)instance, and have that return a reference to the one instance (which you store in a class variable and set the first time when you create the instance, see “singleton pattern”). But without knowing the specifics of your code, I would suggest the first solution (delegate) as simpler and more flexible.

How to generate a generic table view controller?

I've created a custom TablePickerViewController which is a subclass of UITableViewController. I'm using this class to display a list of object of a custom type TablePickerItem.
I'm using TablePickerViewController multiple times in my iOS application to show different kinds of lists where the user has to pick an item -- and then another view controller MainViewController should react on this selection and do something.
I've created this protocol and created a delegate property in the TablePickerViewController:
#protocol TablePickerViewControllerDelegate <NSObject>
- (void)tablePickerViewController:(TablePickerViewController *)controller
didSelectItem:(TablePickerItem*)item;
#end
When I setup a new TablePickerViewController in MainViewController it is also set as delegate -- than it will be notified when the user taps an cell in the table view.
The problem is that my MainViewController will setup multiple TablePickerViewController with different data (TablePickerItem). How should I setup my MainViewController to handle these multiple TablePickerViewController? Events from each of them will results in calling to the same protocol-method in my MainViewController.
Further I need to get the element which the TablePickerItem represents, as I need to know for instance the elements ID when acting in the tablePickerViewController:didSelectItem method. Should I just handle this by adding something like #property (nonatomic) id element to the TablePickerItem and set the original object into this property then creating it?
Maybe someone can give me an example on how to create an generic table view controller, if my solutions seems being done in the wrong way.
I'm not entirely sure of your set up, but if you have multiple pickers that feedback to the main controller then you could just have a reference to the picker e.g.
// MainViewController.m
#interface MainViewController ()
#property (nonatomic, strong) TablePickerViewController *picker1;
#property (nonatomic, strong) TablePickerViewController *picker2;
// ... and so on. Obviously you know your problem domain so you can change
// the terrible naming above to something appropriate
#end
#implementation MainViewController
// ...
- (void)theMethodWhereYouSetUpYourPickers;
{
TablePickerViewController *picker1 = [[TablePickerViewController alloc] init];
picker1.delegate = self;
self.picker1 = picker1;
// ...
}
- (void)tablePickerViewController:(TablePickerViewController *)controller
didSelectItem:(TablePickerItem*)item;
{
if (controller == self.picker1) {
NSLog(#"Something was picked in picker 1 %#", item);
} else if (controller == self.picker2) {
NSLog(#"Something was picked in picker 2 %#", item);
}
}
// ...
#end