I have a controller that gets instantiated and pushed onto the navigation controller like this -
In ParentVC.m
ThingContainerViewController *thingContainer = [[ThingContainerViewController alloc] init];
thingContainer.delegate = self;
[self.navigationController pushViewController:thingContainer animated:YES];
Now I want to send back a value to ParentVC when the thingContainer is closed. So I have implemented a delegate as follows:
in ParentVC.h
#interface ParentVC : UIViewController <..., ThingContainerViewControllerDelegate>
in ThingContainer.h
#protocol ThingContainerViewControllerDelegate <NSObject>
- (void)addItemViewController:(id)controller didFinishEnteringItem:(NSInteger)page;
#end
....
#interface
....
#property (nonatomic, weak) id <ThingContainerViewControllerDelegate> delegate;
....
#end
in ParentVC.m
- (void)addItemViewController:(id)controller didFinishEnteringItem:(NSInteger)page {
self.page = page;
}
in ThingContainer.m
- (IBOutlet) ... closeButtonWasTapped:(UIButton *)button {
NSLog(#"%ld", (long)indexOfPage);
[self.delegate addItemViewController:self didFinishEnteringItem:indexOfPage];
[[self navigationController] popViewControllerAnimated: YES];
}
At this point, self.delegate is null, so the method in the ParentVC never gets called. Any idea how I can fix it?
Related
AddBillViewController.h:
#protocol AddBillDelegate <NSObject>
- (void)addBillViewControllerDidAddOneBill;
#end
#interface AddBillViewController : UIViewController
#property (retain, nonatomic)id <AddBillDelegate> delegate;
#end
AddBillViewController.m:
- (IBAction)AddBill:(id)sender {
[self.delegate addBillViewControllerDidAddOneBill];
[self dismissViewControllerAnimated:YES completion:nil];
}
HomeViewController.m:
#interface HomeViewController () <AddBillDelegate>
#property (strong, nonatomic)AddBillViewController *abVC;
#end
#implementation HomeViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.abVC = [[AddBillViewController alloc] init];
self.abVC.delegate = self;
}
- (void)addBillViewControllerDidAddOneBill {
NSLog(#"Added a bill.");
}
#end
As you can see, there is button in AddBillViewController, when the button is pressed, addBill method will be called, then the delegate method addBillViewControllerDidAddOneBill in HomeViewController should be called, however in fact, it doesn't.
I think you are not getting the reference right here in this line
self.abVC = [[AddBillViewController alloc] init];
If you are performing a segue, like the AddBillViewController is your next controller, set delegate in performSegue: function
like
AddBillViewController *vc = [segue destinationViewController];
[vc setDelegate:self];
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.
I am trying to use a delegate method in AddCity.m controller that I created in the City.m controller. The method is
City.m
- (void)EditCityController:(id)controller didEditItem:(id)item
{
if(item)
{
NSDictionary *d = (NSDictionary *)item;
[self.model addNewCity:[d valueForKey:#"cityName"] forProvince:self.ro inMayor:[d valueForKey:#"mayorName"] inPopulation:[[d valueForKey:#"population"]intValue] inYearEstablished:[[d valueForKey:#"yearEstablished"]intValue]];
[self.model saveChanges];
}
// Dismiss the modal view controller
[controller dismissViewControllerAnimated:YES completion:nil];
}
City.h
#interface Cities :
UITableViewController<NSFetchedResultsControllerDelegate, EditCityDelegate>
And then in AddCity.h i have
#protocol EditCityDelegate;
#interface AddCity : UIViewController<UITextFieldDelegate>
#property (nonatomic, assign) id <EditCityDelegate> delegate;
#protocol EditCityDelegate <NSObject>
- (void) EditCityController:(id)controller didEditItem:(id)item;
#end
I am calling it from AddCity.m (Currently trying to the cancel button).
- (IBAction)cancel:(id)sender {
[self.delegate EditCityController:self didEditItem:nil];
}
When i put a breakpoint what happens is that it hits the self.delegate line but it does not go into the EditCityController method in the City.m controller.Any idea on what i am doing wrong?
I assume you call AddCity.m in City.m and present it using presentViewController
AddCity *addcityVC = [AddCity new];
addcityVC.delegate = self; // initialize the delegate here
[self presentViewController:addCityVC animated:YES completion:nil];
You need to initialize the delegate back to self so it can call the method you need
Hope this helps
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.
I'm new to iOS programming and I can't get this simple concept to work -- I just want the popup controller to be able to call a method and send data on the parent controller. Can anyone spot what I'm doing wrong here?
in DetailViewController.h
#import <UIKit/UIKit.h>
#import "Employee.h"
#import "CompleteViewController.h"
#class EmployeesTVC;
#interface DetailViewController : UIViewController <UISplitViewControllerDelegate, UIPopoverControllerDelegate>
#property (strong) UIPopoverController *popController;
-(IBAction)completeButtonPressed:(id)sender;
#end
in DetailViewController.m (took out irrelevant parts)
#implementation DetailViewController {
__weak UIPopoverController *completePopover;
}
// ...
#pragma mark - Complete / Score popover methods
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
NSLog(#"preparing for segue");
UIStoryboardPopoverSegue *popoverSegue = (UIStoryboardPopoverSegue *)segue;
UIPopoverController *scorePopoverController = [popoverSegue popoverController];
[scorePopoverController setDelegate:self];
self.popController = scorePopoverController;
}
- (IBAction)completeButtonPressed:(id)sender {
if (completePopover) {
[completePopover dismissPopoverAnimated:YES];
} else {
[self performSegueWithIdentifier:#"showScorePopover" sender:sender];
}
}
- (void) scoreAssigned:(NSString *)score {
NSLog(score);
NSLog(#"Score Assigned");
}
// ...
#end
CompleteViewController.h (the popup view controller)
#import <UIKit/UIKit.h>
#protocol CompleteViewDelegate <NSObject>
- (void)scoreAssigned:(NSString *)score;
#end
#interface CompleteViewController : UIViewController
#property (nonatomic, assign) id<CompleteViewDelegate> delegate;
- (IBAction)okButtonPressed:(id)sender;
#end
CompleteViewController.m
#import "CompleteViewController.h"
#implementation CompleteViewController
#synthesize delegate;
- (IBAction)okButtonPressed:(id)sender {
NSLog(#"OK Button Pressed");
[delegate scoreAssigned:#"100"];
}
#end
Is your first NSLog statement firing? "OK Button Pressed". If not, make sure (IBAction)okButtonPressed is wired up in Interface Builder. If so, but a break point on [delegate scoreAssigned:#"100"]; and hover over "delegate" with your mouse to see if it's nil, just to see if the delegate was successfully assigned. If this doesn't fix it, let us know exactly where you're getting to before things stop working.
Properly presenting a UIPopoverController
[completePopover setDelegate:self];
[completePopover presentPopoverFromBarButtonItem:yourButtonItem permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
//or if you don't have a bar button item
[completePopover presentPopoverFromRect:CGRectMake(0.0, 0.0, 0.0, 0.0) inView:yourTargetView permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
Keep in mind that inView can be any subclass of UIView, like UIButton for instance.