I have a UIViewController which is embedded in a navigation controller and presented modally:
//UIViewController
AuthenticationController *auth = [[AuthenticationController alloc] init];
//UINavigationController
AuthRootController *navController = [[AuthRootController alloc]
initWithRootViewController:auth];
navController.navigationBar.topItem.title = #"Anmelden";
navController.delegate = self;
[self presentModalViewController:navController animated:YES];
RELEASE_SAFELY(navController);
However there is something wrong with the delegate I created within the AuthRootController class:
#protocol AuthRootControllerDelegate
#required
-(void)authRootControllerDidEnd:(UINavigationController *)sender;
#end
#interface AuthRootController : UINavigationController {
id<AuthRootControllerDelegate> delegate;
}
#property (nonatomic, assign) IBOutlet id delegate;
#end
And the implementation:
#implementation AuthRootController
#synthesize delegate;
-(void)userDidCancelController:(UINavigationController *)sender{
if (self.delegate && [self.delegate conformsToProtocol:#protocol(AuthRootControllerDelegate)]) {
[self.delegate authRootControllerDidEnd:sender];
}
}
#end
When I use the method
-(void)authRootControllerDidEnd:(UINavigationController *)sender
it is not triggered. Any ideas?
Have you declared that your delegate conforms to AuthRootControllerDelegate? The conformsToProtocol test looks at whether the delegate declares conformance, it doesn't do any sort of method-by-method check. So even if you've implemented authRootControllerDidEnd: on your delegate, conformsToProtocol can still return NO.
In your interface you aren't declaring it as implementing the delegate protocol, you need to modify your interface declaration like this:
#interface AuthRootController : UINavigationController<AuthRootControllerDelegate> {
Related
I am trying to implement a NSWindowController subclass with new xib-file, I read up in lots of books, and researched on StackOverflow, but none of the steps provided made my window show, nor did the subclass code get executed. The new xib-file has its File's Owner set to "LogNavigatorController" and connections to the window and its contents have been made.
My AppDelegate.h:
#import <Cocoa/Cocoa.h>
#class LogNavigatorWindowController;
#interface AppDelegate : NSObject <NSApplicationDelegate>
{
LogNavigatorWindowController *logsWindowController;
}
#end
My AppDelegate.m:
#import "AppDelegate.h"
#import "LogNavigatorWindowController.h"
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
logsWindowController = [[LogNavigatorWindowController alloc] initWithWindowNibName:#"LogNavigatorWindowController"];
[logsWindowController showWindow:self];
}
#end
My LogNavigatorWindowController.h:
#import <Cocoa/Cocoa.h>
#interface LogNavigatorWindowController : NSWindowController
{
NSArray *directoryList1;
NSArray *directoryList2;
NSMutableArray *directoryList;
NSMutableArray *filePaths1;
NSMutableArray *filePaths2;
}
#property (assign) IBOutlet NSWindow *window;
#property (weak) IBOutlet NSTableView *logsTableView;
#property (unsafe_unretained) IBOutlet NSTextView *logsTextView;
#property (assign) IBOutlet NSArrayController *LogListController;
#property (retain) NSMutableArray *logsArray;
- (void) myDirectoryLogFunction;
#end
My LogNavigatorController.m:
#import "LogNavigatorWindowController.h"
#interface LogNavigatorWindowController ()
#end
#implementation LogNavigatorWindowController
#synthesize logsTableView;
#synthesize logsTextView;
#synthesize window;
- (id)init
{
self = [super initWithWindowNibName:#"LogNavigatorWindowController"];
[self loadWindow];
[self showWindow:#"Log Navigator"];
[self.window makeKeyAndOrderFront:nil];
if (self)
{
// Initialization code here.
[self myDirectoryLogFunction];
}
return self;
}
- (void)windowDidLoad
{
[super windowDidLoad];
// Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
}
- (void) myDirectoryLogFunction
{
NSLog(#"Code execution test successful");
}
#end
You don't need to create the window property since it is already available for NSWindowController subclasses. Maybe that causes the problem.
Also your init method contains a lot of code that doesn't belong there. Remove
[self loadWindow];
[self showWindow:#"Log Navigator"];
[self.window makeKeyAndOrderFront:nil];
as well as replace
self = [super initWithWindowNibName:#"LogNavigatorWindowController"];
with
self = [super init];
You may want to remove the init method at all, since you don't need it in your case.
and move
[self myDirectoryLogFunction];
to the windowDidLoad method.
Also always check that the code for instantiating the window controller (in your case from the app delegates didFinishLaunching: ) is called. Sometimes it helps to create a new project and test there, if you may have changed too much within the original project and by accident removed delegate connections or similar.
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.
How to call a RootViewController function from FirstViewController?
I'm using Xcode 4.6 with storyboard.
RootViewController.m:
-(void)openMenu
{
...
}
FirstViewController:
- (IBAction)btnMenu:(id)sender {
RootViewController *root = [[RootViewController alloc] init];
[root openMenu]; // No visible #interface for 'RootViewController' declares the selector 'openMenu'
}
You have to declare the method in your header RootViewController.h. Example
- (void)openMenu;
A common practice for something like this is to use delegation. Your FirstViewController would have a delegate and then your RootViewController would set the delegate for the instance, and receive the information for the event.
FirstViewController.h
#protocol FirstViewDelegate;
#interface FirstViewController : UIViewController
#property (strong) id<FirstViewDelegate> delegate;
#end
#protocol FirstViewDelegate <NSObject>
- (void)openMenu;
#end
FirstViewController.m
- (IBAction)btnMenu:(id)sender {
[self.delegate openMenu];
}
MainViewController.h
#import "FirstViewController.h"
#interface RootViewController : UIViewController
<
FirstViewDelegate
>
MainViewController.m
-(IBAction)showFirstViewButtonClicked:(id)sender {
FirstViewController *firstViewController = [[FirstViewController alloc] init];
firstViewController.delegate = self;
[self presentViewController:firstViewController animated:YES completion:nil];
}
-(void)openMenu {
// this will be called when the btnMenu action is fired in the firstViewController
}
I have a ViewController class with the following structure:
Header file:
#protocol GCStageViewControllerDelegate;
#interface GCStageViewController : UIViewController <UIActionSheetDelegate, UITextFieldDelegate, UIImagePickerControllerDelegate> {
id <GCStageViewControllerDelegate> delegate;
...
}
#property (nonatomic, retain) id <GCStageViewControllerDelegate> delegate;
...
#end
#protocol GCStageViewControllerDelegate
- (void)gcStageViewContollerDidFinish:(GCStageViewController *)controller withGCStageItem:(GCStageItem *)item;
#end
Implementation file:
- (void)viewDidLoad {
...
stageInputTextField.delegate = self; // works
...
}
- (void)takePicture {
UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
[imagePicker setSourceType:UIImagePickerControllerSourceTypeCamera];
} else {
[imagePicker setSourceType:UIImagePickerControllerSourceTypePhotoLibrary];
}
imagePicker.delegate = self; // here I get the error
[self presentModalViewController:imagePicker animated:YES];
}
If I set the delegate in the takePicture method I get the following warning:
Assigning to 'id<UINavigationControllerDelegate,UIImagePickerControllerDelegate>' from incompatible type 'GCStageViewController *'
Any ideas what's wrong?
Apparently UIImagePicker needs you to also implement the UINavigationControllerDelegate protocol. See the documentation, where it clearly shows that the delegate should conform to both. :-)
Luckily, all of the methods in that protocol are optional, so all you need to do it pretend:
// change this
#interface GCStageViewController : UIViewController <UIActionSheetDelegate, UITextFieldDelegate, UIImagePickerControllerDelegate> {
// to this
#interface GCStageViewController : UIViewController <UIActionSheetDelegate, UITextFieldDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate> {
The UIImagePickerControllerDelegate actually requires conformation to two protocols: UIImagePickerControllerDelegate and UINavigationControllerDelegate. Conform to UINavigationControllerDelegate in the header and the error will disappear.
#interface GCStageViewController : UIViewController <UIActionSheetDelegate, UITextFieldDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate>
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).