Need assistance regarding passing data from child to parent in UINavigationcontroller - ios

In UINavigationController this is child controller
.h
#protocol childProtocol <NSObject>
-(void)childMethod:(NSArray*)params;
#end
#property (strong, nonatomic) id<childProtocol>childDelegate;
#property (weak, nonatomic) parentVC *pVC;
.m
if([self.childDelegate respondsToSelector:#selector(childMethod:)]) {
[self.childDelegate performSelector:#selector(childMethod:) withObject:self.arry];
}
This is my parent controller
.m
-(void)childMethod:(NSArray *)params {
// some work
}
...
childVC *cVC = [[childVC alloc]init];
cVC.pVC = self;
But childMethod: is not getting called so I searched on internet and got this post
UINavigationControllers: How to pass value to higher (parent?) controller in stack?
I tried to create a weak reference but dont know how to use to make delegate pass data from child to parent?

Try this. Check the sample project attached
ParentViewController.h
#import <UIKit/UIKit.h>
#interface ParentViewController : UIViewController
- (void)passData:(NSString *)strText;
#end
ParentViewController.m
- (IBAction)btnGoToSecondView:(id)sender {
ChildViewController *secondVC = [[ChildViewController alloc] initWithNibName:#"ChildViewController" bundle:nil];
secondVC.delegate = self;
[self presentViewController:secondVC animated:YES completion:nil];
}
- (void)passData:(NSString *)strText {
NSLog(#"Data Passed = %#",strText);
}
ChildViewController.h
#import <UIKit/UIKit.h>
#import "ParentViewController.h"
#class ParentViewController;
#interface ChildViewController : UIViewController
#property(nonatomic, assign) ParentViewController *delegate;
#end
ChildViewController.m
- (IBAction)btnPassDataBack:(id)sender {
if([self.delegate respondsToSelector:#selector(passData:)]) {
[self.delegate passData:#"Hello"];
}
[self dismissViewControllerAnimated:YES completion:nil];
}
Sample Project

This is child controller.h
#protocol childProtocol <NSObject>
-(void)childMethod:(NSArray*)params;
#end
#property (strong, nonatomic) id<childProtocol>childDelegate;
#property (weak, nonatomic) parentVC *pVC;
.m
if([self.childDelegate respondsToSelector:#selector(childMethod:)]) {
[self.childDelegate performSelector:#selector(childMethod:) withObject:self.arry];
}
This is my parent controller.h
#import <UIKit/UIKit.h>
#import "ChildController.h"
#interface perentController : UIViewController < childProtocol >
.m
- (void)childMethod:(NSArray *)params {
// some work
}
EDITED :
And Dont Forget to add childViewOBJ.childDelegate = self; at the time of create ChildViewController's object. such like,
childVC *cVC = [[childVC alloc]init];
cVC.childDelegate = self;
cVC.pVC = self;
[self presentModalViewController:cVC animated:YES];
For More information about How to create/use of Protocol.

First of all, you are not checking for the same selector as you declared in your protocol declaration so it won't respond to that. You declared the method childMethod: whereas you are checking if your childDelegate responds to myMethod: selector which does not so it won't go into the if condition.
Also the parent view controller is missing the implementation the method childMethod: in its .m. Implement that in your parent view controller or it will crash because of not finding the exact selector definition.
Since you are using a UINavigationController, the parent view controller won't be lost till the child view controller exist so the childDelegate property must not be strong unless you intend to hold onto your delegate in child view controller for some reason.

Related

Unable to pass data to the previous viewcontroller

Im trying to implement delegate design pattern.
I have two view controllers as followsm
CallViewViewController
CEPeoplePickerNavigationController
This is my interface definition of CallViewViewController
#interface CallViewViewController ()<CEPeoplePickerNavigationControllerDelegate>{
}
#property(nonatomic)CustomMessageClass * customMessageClassObject;
#end
In my implementation, i have of-course implemented the delegate method
-(void)cePeoplePickerNavigationController:(CEPeoplePickerNavigationController *)peoplePicker didFinishPickingPeople:(NSArray *)peopleArg values:(NSDictionary *)valuesArg
{
NSLog(#"can populate the tableview");
}
This is the interface definition of my second class,
#protocol CEPeoplePickerNavigationControllerDelegate;
#interface CEPeoplePickerNavigationController : UIViewController <UITableViewDelegate,UITableViewDataSource>{
id<CEPeoplePickerNavigationControllerDelegate> peoplePickerDelegate;
ABAddressBookRef addressBook;
}
#property (nonatomic, assign) id<CEPeoplePickerNavigationControllerDelegate> peoplePickerDelegate;
#property (nonatomic, retain) CEPeoplePickerNavigationController *ppnc;
#end
#protocol CEPeoplePickerNavigationControllerDelegate <NSObject>
- (void)cePeoplePickerNavigationController:(CEPeoplePickerNavigationController *)peoplePicker didFinishPickingPeople:(NSArray*)people values:(NSDictionary *)values;
#end
when the user presses submit button,Im executing the following code,
if ([self.ppnc.peoplePickerDelegate respondsToSelector:#selector(cePeoplePickerNavigationController:didFinishPickingPeople:values:)])
[self.ppnc.peoplePickerDelegate cePeoplePickerNavigationController:self.ppnc didFinishPickingPeople:sortedPeople values:[NSDictionary dictionaryWithDictionary:self.selectedValues]];
But the data is not being passed back to the previous view controller. Why so?
UPDATE
i tried the following code to move form first to second view controller,
CEPeoplePickerNavigationController *nextVC = [self.storyboard instantiateViewControllerWithIdentifier:#"PeoplePickerNavigationController"];
nextVC.peoplePickerDelegate = self;
[self presentViewController:nextVC animated:YES completion:nil];
but it throws the following error,
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UINavigationController setPeoplePickerDelegate:]: unrecognized selector sent to instance 0x101054800'
You just need to pass data using Custom Delegate
Write Below code on your CEPeoplePickerNavigationController's .h file
#class CEPeoplePickerNavigationController;
#protocol CEPeoplePickerNavigationController <NSObject>
- (void)previousViewController:(CEPeoplePickerNavigationController *)controller itemToSend:(NSString *)item;
#end
#interface CEPeoplePickerNavigationController : UIViewController
#property (nonatomic, weak) id < CEPeoplePickerNavigationController Delegate> delegate;
#end
on your moving to previous view controller event you need to put below code
[self.delegate previousViewController:self itemToSend:#"From Previous VC"];
[self.navigationController popViewControllerAnimated:true];
On your CallViewViewController you just need to add below code on .m file
#import "CEPeoplePickerNavigationController.h"
#interface ViewController ()< CEPeoplePickerNavigationController Delegate>
Add method declare on previous view controller
- (void)previousViewController:(CEPeoplePickerNavigationController *)controller itemToSend:(NSString *)item
{
NSLog(#"from CEPeoplePickerNavigationController %#",item);
}
make sure when you navigate to CEPeoplePickerNavigationController you need to assign delegate to self as below
CEPeoplePickerNavigationController *objVC = [self.storyboard instantiateViewControllerWithIdentifier:#"CEPeoplePickerNavigationController"];
objVC.delegate = self;
[self.navigationController pushViewController:infoController animated:YES];
When you are moving from CallViewViewController to CEPeoplePickerNavigationController , there must be some sort of navigation where you are pushing or applying segue to go the next VC.
Just add this line of code there -
If you are using segue --
CEPeoplePickerNavigationController *nextVC = [segue destinationViewController];
nextVC.peoplePickerDelegate = self;
If you are using instantiate --
CEPeoplePickerNavigationController *nextVC = [self.storyboard instantiateWithStoryboardid:#"YOUR STORYBOARD ID"];
nextVC.peoplePickerDelegate = self;
Once this is done, it will respond to the delegate from the next controller
EDIT
Please clear your CEPeoplePickerNavigationController .h code to the following
#protocol CEPeoplePickerNavigationControllerDelegate <NSObject>
- (void)cePeoplePickerNavigationController:(CEPeoplePickerNavigationController *)peoplePicker didFinishPickingPeople:(NSArray*)people values:(NSDictionary *)values;
#end
#interface CEPeoplePickerNavigationController : UIViewController <UITableViewDelegate,UITableViewDataSource>{
ABAddressBookRef addressBook;
}
#property (nonatomic, assign) id<CEPeoplePickerNavigationControllerDelegate> peoplePickerDelegate;
#property (nonatomic, retain) CEPeoplePickerNavigationController *ppnc;
#end

UISplitViewController using delegate to pass a string

I'm using a Split View Controller for an iPad app. I'm trying to send a label change to the detailReceiving Controller from the rootSending Controll when a button is pushed. I've read through tutorials on protocols and came up with the code below. When I click the button on rootSending, nothing happens to the label on detailReceiving. Do I have to do something else with a splitViewContoller so that the label will update? Shouldn't detailReceiving change the label when it receives the message?
rootSending.h
#import <UIKit/UIKit.h>
#protocol TestDelegate <NSObject>
-(void)tester:(NSString*)testString;
#end
#interface rootSending : UIViewController
#property (nonatomic, assign) id <TestDelegate> delegate;
#end
rootSending.m
#import "rootSending.h"
#implementation rootSending
#synthesize delegate;
-(void)viewDidLoad{
}
-(IBAction)buttonPressed:(id)sender{
[delegate tester:#"button pressed"];
}
#end
detailReceiving.m
#import "detailReceiving.h"
#import "rootSending.h"
#interface detailReceiving ()<TestDelegate>{
IBOutlet UILabel *label2;
}
#end
#implementation detailReceiving
-(void)viewDidLoad{
rootSending *obj = [rootSending alloc];
obj.delegate = self ;
}
-(void)tester:(NSString *)testString{
label2.text = testString;
}
#end
First of all, never ever have an alloc without an init! But in this case, even if you did use alloc/init, it still wouldn't work because that just creates a new instance of rootSending, not the one that you have in your split view. You need to get a reference to the one you have, which you can get from the split view controller,
-(void)viewDidLoad{
rootSending *obj = (rootSending *)self.splitViewController.viewControllers.firstObject;
obj.delegate = self;
}
After Edit:
If your mate controller is embedded in a navigation controller, then you need to get the navigation controller's topViewController to get your reference.
-(void)viewDidLoad{
UINavigationController *nav = (UINavigationController *)self.splitViewController.viewControllers.firstObject;
xmlListOfItems *obj = (xmlListOfItems *)nav.topViewController;
obj.delegate = self;
}

Control values overridden by storyboard setting

I have a VC of custom type LVSBBSettingsViewController for user settings. The VC is presented by a main menu in LVSMainViewController. The main VC sets the values of the controls in the settings VC programatically. However, when the settings view appears, the controls all revert to the values assigned to them in the storyboard.
I am using delegation to close the settings view and to pass data from the settings VC back to the main VC when it closes. But I don't think that's what's causing the problem since the same thing happens even if I remove that.
What's causing this? I have a feeling I'm missing something really simple here...
LVSBBSettingsViewController.h:
#import <UIKit/UIKit.h>
#class LVSBBSettingsViewController;
#pragma mark LVSBBSettingsViewController Delegate
#protocol LVSBBSettingsViewControllerDelegate <NSObject>
- (void)settingsViewControllerDidCancel:(LVSBBSettingsViewController *)controller;
- (void)settingsViewControllerDidSave:(LVSBBSettingsViewController *)controller;
#end
#pragma mark LVSBBSettingsViewController
#interface LVSBBSettingsViewController : UITableViewController
#property (nonatomic, weak) id <LVSBBSettingsViewControllerDelegate> delegate;
#property (weak, nonatomic) IBOutlet UISwitch *showBranchVarLabelsSwitch;
#property (weak, nonatomic) IBOutlet UISwitch *useAnimationSwitch;
#property (weak, nonatomic) IBOutlet UISwitch *showAllNodesSwitch;
#property (weak, nonatomic) IBOutlet UILabel *tempLabel;
- (IBAction)cancel:(id)sender;
- (IBAction)done:(id)sender;
#end
LVSBBSettingsViewController.m:
#import "LVSBBSettingsViewController.h"
#interface LVSBBSettingsViewController ()
#end
#implementation LVSBBSettingsViewController
// ... Xcode-generated stuff ...
- (IBAction)cancel:(id)sender
{
[self.delegate settingsViewControllerDidCancel:self];
}
- (IBAction)done:(id)sender
{
[self.delegate settingsViewControllerDidSave:self];
}
#end
LVSBBMainViewController.h:
#import <UIKit/UIKit.h>
#import "LVSBBSettingsViewController.h"
#interface LVSMainViewController : UIViewController <LVSBBSettingsViewControllerDelegate>
#end
LVSBBMainViewController.m:
#import "LVSMainViewController.h"
#import "LVSBBMasterViewController.h"
#interface LVSMainViewController ()
#end
#implementation LVSMainViewController
{
LVSBBMasterViewController *bbmvc;
}
// ...
- (void)viewDidLoad
{
[super viewDidLoad];
// Get main storyboard
UIStoryboard *st = [UIStoryboard storyboardWithName:[[NSBundle mainBundle].infoDictionary objectForKey:#"UIMainStoryboardFile"] bundle:[NSBundle mainBundle]];
// Instantiate bbmvc
bbmvc = [st instantiateViewControllerWithIdentifier:#"BBMasterViewControllerStoryboard"];
// Initialize settings
bbmvc.showBranchVarLabels = YES;
bbmvc.useAnimation = YES;
bbmvc.showAllNodes = NO;
}
...
#pragma mark LVSBBSettingsViewController Delegate
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"ShowSettings"])
{
// Get pointer to settings VC
UINavigationController *navigationController = segue.destinationViewController;
LVSBBSettingsViewController *settingsViewController = [navigationController viewControllers][0];
// Set delegate
settingsViewController.delegate = self;
// Populate settings VC
// (same problem occurs if I replace right-hand sides of next 3 lines with NO;)
settingsViewController.showBranchVarLabelsSwitch.on = bbmvc.showBranchVarLabels;
settingsViewController.useAnimationSwitch.on = bbmvc.useAnimation;
settingsViewController.showAllNodesSwitch.on = bbmvc.showAllNodes;
settingsViewController.tempLabel.text = #"HELLO";
}
}
- (void)settingsViewControllerDidCancel:(LVSBBSettingsViewController *)controller
{
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)settingsViewControllerDidSave:(LVSBBSettingsViewController *)controller
{
// Set settings in bbmvc
bbmvc.showBranchVarLabels = controller.showBranchVarLabelsSwitch.on;
bbmvc.useAnimation = controller.useAnimationSwitch.on;
bbmvc.showAllNodes = controller.showAllNodesSwitch.on;
[self dismissViewControllerAnimated:YES completion:nil];
}
#end
UPDATE: As a workaround, I added properties in LVSBBSettingsViewController that match the properties in LVSMainViewController. In prepareForSegue:sender:, I set those properties instead of setting the controls directly. Then in viewDidLoad in LVSBBSettingsViewController, I set the control values based on the properties. This seems to work. Still not sure why I can't set the control values directly, though.

delegate respondsToSelector:selector not working

I saw a lot of this kind of questions and answers here, but couldn't find solution to my problem. I'm trying to send data from one view controller to another and use delegate. But don't know why my postDelegate doesn't responds to selector. Is something wrong with this code or what is the problem?
PostViewController.h file
#protocol GetDataDelegate <NSObject>
-(void)getPassedInfo:(NSString*)info;
#end
#interface PostViewController : UIViewController
#property (nonatomic, weak) id <GetDataDelegate> postDelegate;
#end;
PostViewController.m file
#import "PostViewController.h"
- (IBAction)postData:(id)sender {
if ([_postDelegate respondsToSelector:#selector(getPassedInfo:)]) {
[self.postDelegate getPassedInfo:#"data"];
NSLog(#"responds");
}
[self dismissViewControllerAnimated:YES completion:nil];
}
in second view controllers .h file
#import "PostViewController.h"
#interface MainViewController : UITableViewController <GetDataDelegate>
and in .m file
#implementation MainWindowTableViewController
- (void)viewDidLoad
{
[super viewDidLoad];
PostViewController * postController = [[PostViewController alloc]init];
postController.postDelegate = self;
}
and here is delegate method:
-(void)getPassedInfo:(NSString *)info{
NSLog(#"info is %#", info);
}
You need to make postController a property or an ivar. Currently it is a local variable in the viewDidLoad method which will be deallocated after viewDidLoad completes as #CodaFi said above.
#import "PostViewController.h"
#interface MainViewController : UITableViewController <GetDataDelegate>
#property (nonatomic, strong) PostViewController *postController;
#end
Then:
- (void)viewDidLoad
{
[super viewDidLoad];
self.postController = [[PostViewController alloc]init];
self.postController.postDelegate = self;
}

Accessing another viewcontroller

I'm trying to access some things from another viewcontroller (iOS).
I have my ViewController.h:
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController <UITextFieldDelegate> {
...
}
#end
ViewController.m:
#import "ViewController.h"
#import "ViewController2.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
ViewController2.someVar = #"cakes"; // this is where I'm trying to set something in vc2
}
ViewController2.h:
#import <UIKit/UIKit.h>
#interface ViewController2 : UIViewController <UITableViewDataSource, UITableViewDelegate, UISearchDisplayDelegate>
{
...
}
#property (nonatomic, copy) NSString *someVar;
#end
ViewController2.m:
#import "ViewController2.h"
#interface ViewController2 ()
#end
#implementation ViewController2
#synthesize someVar;
etc.
But at the line where I try to access this var, it gives me the following error:
Property 'someVar' not found on object of type 'ViewController2'.
In what way would I achieve accessing this other view controller?
You have to create a ViewControlle2 object in your Viewcontroller.m.
ViewController2 *myVC2 = [[ViewController2 alloc] initWithNibName:#"ViewController2" bundle:nil];
myVC2.someVar = #"cakes";
Another way to do this is by using delegates. If you created ViewController1 in AppDelegate, you should also create ViewController2 in AppDelegate, or where ever you create your ViewControllers. Then you ViewController1 would send a message to AppDelegate to get the data from ViewController2 and vice versa. This makes ViewController1 and ViewController2 no longer dependent on each other.
So in AppDelegate.h we'd have something like this
#interface AppDelegate : NSObject <MyViewControllerDelegate>
#property (nonatomic) ViewController1 *viewController1;
#property (nonatomic) ViewController2 *viewController2;
#end
Then in both view controllers you can add this line in the .h
#property (nonatomic) id<MyViewControllerDelegate> delegate;
This just gives us a variable to use to refer to the delegate from within the ViewController's .m
You also need to create the MyViewControllerDelegate protocol, so in a file called MyViewControllerDelegate.h
#protocol MyViewControllerDelegate
- (ViewController1 *) viewController1;
- (ViewControlelr2 *) viewController2;
#end
Then when you create the ViewControllers in AppDelegate.m, you should also set AppDelegate as the ViewController's delegate.
self.viewController1 = [[ViewController1 alloc] init];
self.viewController1.delegate = self;
self.viewController2 = [[ViewController2 alloc] init];
self.viewController2.delegate = self;
So with all of this delegate set up done, you should be able to access viewController2.someVar from viewController1 through the delegate by using:
self.delegate.viewController2.someVar = #"Cakes";
Hope this isn't too long winded.

Resources