Objective-C: Delegate is nil in modal in navigation controller - objective-c

I've got a ViewController inside a navigation controller that needs to present a modal.
The ViewController has this as its header:
#interface ViewController : BaseViewController<AuthenticateDelegate>
and in the IBAction that presents the modal:
AuthenticationController *authVC = [self.storyboard instantiateViewControllerWithIdentifier:#"AuthControllerView"];
authVC.delegate = self;
[self presentModalViewController:authVC animated:YES];
The AuthenticationController has this in its .h file:
#interface AuthenticationController: BaseViewController<UITextFieldDelegate>
#property (nonatomic, assign) id <AuthenticateDelegate> delegate;
#end
As you can see, I have assigned "self" (the ViewController) as the delegate to the AuthenticationController, but for some reason, the delegate is in:
- (IBAction)SubmitAuthentication:(id)sender;
{
[self.delegate validateUser:lblUsername.text :lblPassword.text];
[self dismissModalViewControllerAnimated:YES];
}
Any help will be appreciated.

you must create delegate property as below.
#property (nonatomic, strong) id <AuthenticateDelegate> delegate;

Related

NSButton IBAction Crash unrecognized selector

Running into a simple problem and not quite sure what is causing it. Linked a NSButton up to a ViewController xib. The property is referenced and then I linked up the IBAction to the view controllers view. I'm getting a crash whenever I press the button with an unrecognized selector message. I know I'm doing something wrong but on iOS this is pretty standard.
Here is the code:
#import "AppDelegate.h"
#interface AppDelegate ()
#property (weak) IBOutlet NSWindow *window;
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
IPVLocationViewController *mainViewController = [[IPVLocationViewController alloc]initWithNibName:#"IPVLocationViewController" bundle:nil];
self.window.contentView = mainViewController.view;
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
}
#end
#import "MainViewController.h"
#interface MainViewController ()
#property (weak) IBOutlet NSButton *mainButton;
#end
#implementation MainViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do view setup here.
}
- (IBAction)mainClick:(id)sender {
NSLog(#"THE BUTTON WAS CLICKED");
}
#end
In your Nib , it looks like your view controller instance is of type NSViewController instead of MainViewController.
So select the view controller in your Nib (or storyboard), and change its type to MainViewController.
Of course, if this view controller isn't being loaded from a nib or storyboard, then just check where you create it and make sure you created an instance of the correct class.
Solved it:
The accepted answer in this post helped me:
Needed to hold a reference to the view controller in the AppDelegate.
#interface AppDelegate ()
#property (nonatomic, strong) MainViewController *mainViewController;
#property (weak) IBOutlet NSWindow *window;
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
self.mainViewController = [[MainViewController alloc]initWithNibName:#"MainViewController" bundle:nil];
self.window.contentView = self.mainViewController.view;
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
}
#end

Objective-C Protocols Not Sending Message

I have read around, and it seems as though delegates would be really useful in my app. Unfortunately, every tutorial about protocols I have tried has failed - the delegate is not receiving the message! It would be great if someone could tell me what I'm doing wrong.
I created a really simple test app with two ViewControllers, a FirstViewController and a SecondViewController. I have set them up in container views to see the effect properly.
My Main.storyboard looks like this:
The purpose of the test app is to change the background colour of the SecondViewController when one of the buttons is pressed in the FirstViewController.
Here is FirstViewController.h:
#import <UIKit/UIKit.h>
#protocol FirstViewControllerDelegate
-(void)colourDidChange:(UIColor *)theColour;
#end
#interface FirstViewController : UIViewController{
UIButton *redButton;
UIButton *blueButton;
}
#property (nonatomic, retain) id <FirstViewControllerDelegate> delegate;
#property (nonatomic, retain) IBOutlet UIButton *redButton;
#property (nonatomic, retain) IBOutlet UIButton *blueButton;
-(IBAction)redPressed;
-(IBAction)bluePressed;
My FirstViewController.m:
#import "FirstViewController.h"
#interface FirstViewController ()
#end
#implementation FirstViewController
#synthesize redButton, blueButton;
#synthesize delegate;
-(IBAction)redPressed{
[self.delegate colourDidChange:[UIColor redColor]];
}
-(IBAction)bluePressed{
[self.delegate colourDidChange:[UIColor blueColor]];
}
-(void)viewDidLoad
{
[super viewDidLoad];
}
I think I have implemented the protocol and the calling of the delegate correctly.
Here is my SecondViewController.h:
#import <UIKit/UIKit.h>
#import "FirstViewController.h"
#interface SecondViewController : UIViewController <FirstViewControllerDelegate>
-(void)colourDidChange:(UIColor *)theColour;
And my SecondViewController.m:
-(void)colourDidChange:(UIColor *)theColour{
self.view.backgroundColor = theColour;
}
-(void)viewDidLoad
{
[super viewDidLoad];
FirstViewController *firstView = [[FirstViewController alloc]init];
firstView.delegate = self;
}
I have breakpointed the project and realised that colourDidChange: in the SecondViewController is never executed.
It would be much appreciated if someone could point out what I have done wrong, whether declaring (or conforming to) the delegate poorly or not setting the delegate the right way.
Many thanks.
I suspect that there are 2 instances of FirstViewController, one created by your storyboard and another one created in SecondViewController's viewDidLoad method.
When theFirstViewController creates SecondViewController it could set the delegate property or use an Outlet to connect them.
Note: delegate properties should not be retain, they should be assign (or weak with ARC).
You are honestly very close. Container views will call the prepareForSegue: method, so you should be initializing the second view controller's delegate in this method:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"TypeContainerViewSegueNameHere"]) {
SecondViewController *viewController = (SecondViewController *)segue.destinationViewController;
viewController.delegate = self;
}
}
This way you know that you are getting the instance of SecondViewController that will be in use. Also, you do not need to redeclare the delegate method in your SecondViewController.h file:
-(void)colourDidChange:(UIColor *)theColour;
Finally, in storyboard set the title of the container view segue to SecondViewController to whatever title you like and then copy paste that title to where 'TypeContainerViewSegueNameHere' is written above.
EDIT 1:
A typical situation would be similar to this:
#protocol ViewControllerDelegate;
#interface ViewController : UIViewController
#property (nonatomic, strong) id<ViewControllerDelegate>delegate;
#end
#protocol ViewControllerDelegate <NSObject>
- (void) delegateMethod;
#end
...
#implementation ViewController
- (void) buttonAction:(id)sender {
[self.delegate delegateMethod];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"TypeContainerViewSegueNameHere"]) {
SecondViewController *viewController = (SecondViewController *)segue.destinationViewController;
viewController.delegate = self;
}
}
#end
...
#interface SecondViewController : UIViewController <ViewControllerDelegate>
#end
...
#implementation SecondViewController
- (void)delegateMethod {
}
#end
That said, you could make your main view controller the delegate of your FirstViewController, which has the two view containers as seen in your screenschot. And then call a delegate method from the main view controller to the second view controller. Although I am curious as to why you have these two view controllers as child view controllers rather than placing a view and two buttons in one view controller.
EDIT 2:
Here is an example (written quickly and not tested). Think of it as a triangle of delegates:
#protocol FirstViewControllerDelegate;
#interface FirstViewController : UIViewController
#property (nonatomic, strong) id<FirstViewControllerDelegate>delegate;
#end
#protocol FirstViewControllerDelegate <NSObject>
- (void) firstViewControllerDelegateMethod;
#end
...
#implementation FirstViewController
- (void) buttonAction:(id)sender {
[self.delegate firstViewControllerDelegateMethod];
}
#end
...
#protocol MainViewControllerDelegate;
#interface MainViewController : UIViewController <FirstViewControllerDelegate>
#end
#protocol MainViewControllerDelegate <NSObject>
- (void) mainViewControllerDelegateMethod;
#end
...
#implementation MainViewController
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"TypeContainerViewSegueNameHere"]) {
SecondViewController *viewController = (SecondViewController *)segue.destinationViewController;
viewController.delegate = self.delegate;
}
if ([segue.identifier isEqualToString:#"TypeContainerViewSegueNameHere"]) {
FirstViewController *viewController = (FirstViewController *)segue.destinationViewController;
viewController.delegate = self;
}
}
- (void)firstViewControllerDelegateMethod {
[self.delegate mainViewControllerDelegateMethod];
}
#end
...
#interface SecondViewController : UIViewController <MainViewControllerDelegate>
#end
...
#implementation SecondViewController
- (void)mainViewControllerDelegateMethod {
}
#end
Like I said, you should think about reducing the complexity of this section of your app and consider putting all of your views in one view controller.

Delegate to 'Parent' ViewController best practice

The setup:
PickerView (spinSelector) and label (chosenItem) added to ViewController.
Created separate delegate class files (SpinDelegate m&h) for the PickerView delegate.
Created instance of the delegate (SpinDelegate *mySpinDelegate)
Assigned delegate property to delegate instance
ViewController.h
#interface ViewController : UIViewController
{
SpinDelegate *mySpinDelegate;
}
#property (nonatomic, weak) IBOutlet UILabel *chosenItem;
#property (nonatomic, strong) IBOutlet UIPickerView *spinSelector;
ViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
mySpinDelegate=[[SpinDelegate alloc]init];
self.spinSelector.delegate=mySpinDelegate;
self.spinSelector.dataSource=mySpinDelegate;
}
SpinDelegate.h
#interface SpinDelegate : UIViewController <UIPickerViewDataSource, UIPickerViewDelegate>
{
ChoiceData *choiceItems;
}
#end
SpinDelegate.m
#pragma mark - PickerView Delegate
- (NSString *)pickerView:(UIPickerView *)pickerView
titleForRow:(NSInteger)row
forComponent:(NSInteger)component{
return [choiceItems.choiceList objectAtIndex:row];
}
Next is to use the method:
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component;
This is part of the UIPickerViewDelegate protocol. Using this, to simply change the UILabel (chosenItem) in the ViewController to value of row. Something like "The picked item is %i",row
I've read and searched through a ton of posts and questions on UIViewController to UIViewController messaging and looked at protocol/delegate, singleton, NSNotificationCenter... it just seems to me that there is a syntax I am missing to address the parent/super? The ViewController created the instance of the delegate, doesn't the delegate have scope?
Please educate me on this. : )
You can do something like this:
ViewController.h
#import "SpinViewController.h"
#interface ViewController : UIViewController<SpinViewControllerDelegate>
{
}
#property (nonatomic, weak) IBOutlet UILabel *chosenItem;
#property (nonatomic, strong) IBOutlet UIPickerView *spinSelector;
ViewController.m
- (void)someFunction
{
mySpinViewController=[[SpinViewController alloc]init];
mySpinViewController.delegate=self;
// show or present mySpinViewController
}
//implement the followed protocol's method
-(void) optionSelected:(NSString*)cellValue{
}
SpinViewController.h
#protocol SpinViewControllerDelegate <NSObject>
#optional
-(void) optionSelected:(NSString*)cellValue;
#end
#interface SpinViewController : UIViewController <UIPickerViewDataSource, UIPickerViewDelegate>
{
ChoiceData *choiceItems;
}
#property (nonatomic,retain)id <SpinViewControllerDelegate> delegate;
#end
SpinViewController.m
-(void) pickerValueSelectedOrSimilarFn:(NSString*)cellValue{
// this is how you give a callback to classes following the protocol
[self.delegate optionSelected:cellValue];
}

objective C: Using a Delegate to call a function in parent class

I'm creating a 3 layer navigation popup controller and on the 3rd popup controller I have a delegate method to access dismissPopup method that is in the parent class. I can't seem to call it, my NSLog messages in the function in the parent class isn't even showing so I must be either using delegation wrong or I'm calling it incorrectly.
The 3 classes ParentViewController has a toolbar with a button that brings up the table view --> RegionViewController is the First table view controller with items --> ConusViewController is the 2nd table view controller that is pushed onto the navigation stack. I'm trying to call the method dismissPopover that is in the parent method with a delegation after the selection is clicked on so the whole popover goes away.
In the ConusViewController if the delegation had worked I would have seen "Method Accessed" from the function in the parent class. It doesn't show so I must be using delegation wrong.
Sorry for being so wordy on my post, I wanted to be complete on what I'm trying to do here. Thanks.
ParentViewController.h
#import <UIKit/UIKit.h>
#import "ConusViewController.h"
#interface EnscoWXViewController : UIViewController <ConusViewControllerDelegate> {
UIPopoverController *popoverController;
IBOutlet UIWebView *webImageDisplay;
ConusViewController *cViewController;
}
#property (nonatomic, retain) UIPopoverController *popoverController;
#property (nonatomic, retain) UIWebView *webImageDisplay;
#property (nonatomic, retain) ConusViewController *cViewController;
-(IBAction) buttonShowRegion:(id) sender;
#end
ParentViewController.m
#import "ParentViewController.h"
#import "RegionViewController.h"
#implementation ParentViewController
#synthesize cViewController;
-(IBAction) buttonShowRegion:(id) sender {
...
}
-(void)dismissPopover {
[popoverController dismissPopoverAnimated:YES];
printf("Method Accessed\n");
}
- (void)viewDidLoad {
cViewController = [[ConusViewController alloc] init];
cViewController.delegate = self;
[super viewDidLoad];
}
RegionViewController.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
switch (indexPath.row) {
case 0: {
ConusViewController *conusViewController = [[ConusViewController alloc] initWithNibName:#"ConusViewController" bundle:nil];
conusViewController.contentSizeForViewInPopover = CGSizeMake(320, 350);
[self.navigationController pushViewController:conusViewController animated:YES];
[conusViewController release];
break;
}
case 1: {
break;
}
}
}
ConusViewController.h
#import <UIKit/UIKit.h>
#protocol ConusViewControllerDelegate <NSObject>
#required
- (void)dismissPopover;
#end
#interface ConusViewController : UITableViewController {
NSMutableArray *conusItems;
id delegate;
}
#property (nonatomic, assign) id <ConusViewControllerDelegate> delegate ;
#end
ConusViewController.m
#import "ConusViewController.h"
#import "ParentWXViewController.h"
#implementation ConusViewController
#synthesize delegate;
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *itemRequested = [conusItems objectAtIndex:indexPath.row];
NSLog(#"logging: %#", itemRequested);
[delegate dismissPopover];
[itemRequested release];
}
Just before calling [delegate dismissPopover], check if delegate is actually set. It probably isn't.
I see in ParentViewController.m you create an instance of ConusViewController and set its delegate, but never display it. In RegionViewController.m you create another instance of ConusViewController without setting its delegate and that is the one that seems to be being displayed.
Not sure if I missed it, but I never see you set the delegate property in ConusViewController. That needs to be set to an instance of the object that is to be delegated to (the object that has dismissPopover implemented in it).

Delegation and Modal View Controllers

According to the View Controller Programming Guide, delegation is the preferred method to dismiss a modal view.
Following Apple's own Recipe example, i have implemented the following, but keep getting warnings that the addNameController:didAddName method is not found...
NameDelegate.h
#protocol NameDelegate
- (void)addNameController:(AddName *)addNameController didAddName:(NSString *)name;
#end
AddName.h
#interface AddName : UIViewController {
UITextField *nameField;
id delegate;
}
- (IBAction)doneAction;
- (id)delegate;
- (void)setDelegate:(id)newDelegate;
#property (nonatomic, retain) IBOutlet UITextField *nameField;
#end
AddName.m
- (IBAction)doneAction {
[delegate addNameController:self didAddName:[nameField text]];
}
- (id)delegate {
return delegate;
}
- (void)setDelegate:(id)newDelegate {
delegate = newDelegate;
}
ItemViewController.h
#import "NameDelegate.h"
#interface ItemViewController : UITableViewController <NameDelegate>{
}
#end
ItemViewController.m
- (void)addItem:(id)sender {
AddName *addName = [[AddName alloc] init];
addName.delegate = self;
[self presentModalViewController:addName animated:YES];
}
- (void)addNameController:(AddName *)addNameController didAddName:(NSString *)name {
//Do other checks before dismiss...
[self dismissModalViewControllerAnimated:YES];
}
I think all the required elements are there and in the right place?
Thanks
You haven't specified that the delegate property of AddName has to conform to the NameDelegate protocol.
Use this code in AddName.h:
#import "NameDelegate.h"
#interface AddName : UIViewController {
UITextField *nameField;
id <NameDelegate> delegate;
}
#property(nonatomic, retain) IBOutlet UITextField *nameField;
#property(nonatomic, assign) id <NameDelegate> delegate;
- (IBAction)doneAction;
#end