Objective-C delegate function not working correctly - objective-c

I have an app that has three views. All three views have an Ad Banner at the bottom of the screen. The first view creates an audio streamer which is paused when the Ad Banner on this view is clicked. I'm trying to use the AdBanner delegate methods on the second view to stop/start the audio. When the Ad Banner is selected the AdBanner delegate methods should call my custom delegate functions. The code compiles and runs but doesn't function correctly.
Using NSLog I've determined that the Ad Banner is calling its delegate function correctly but this isn't calling the custom delegate.
Hope this makes sense. Any help would be appreciated. Here is my code.
SecondViewControler H-file
#import "SecondViewController.h"
#protocol demoViewControllerDelegate <NSObject>
#required
-(void)stopSent;
-(void)startSent;
#end
#interface SecondViewController ()
{
id<demoViewControllerDelegate> delegate;
}
#property (nonatomic, assign) id<demoViewControllerDelegate> delegate;
#end
SecondViewController M-file
#implementation SecondViewController
#synthesize delegate;
Protocols
- (BOOL)bannerViewActionShouldBegin:(ADBannerView *)banner willLeaveApplication:(BOOL)willLeave {
[delegate stopSent];
return YES;
}
- (void)bannerViewActionDidFinish:(ADBannerView *)banner{
[delegate startSent];
}
FirstViewController H-file
#import <QuartzCore/QuartzCore.h>
#import "iAd/iAd.h"
#import <MessageUI/MessageUI.h>
#import "AudioStreamer.h"
#import "Reachability.h"
#import "SecondViewController.h"
#import "MFAppDelegate.h"
#import "MFSideMenu.h"
Class secondViewConroller;
#interface DemoViewController : UIViewController <ADBannerViewDelegate,demoViewControllerDelegate> {
}
#end
FirstViewController M-file
-(void)stopSent{
if (isPlaying) {
[streamer stop];
wasPlaying=true;
}
}
-(void)startSent{
if (wasPlaying) {
[streamer start];
isPlaying=true;
}
}

Your protocol methods need to be implemented in the class that you've designated as your delegate target.
It looks like your DemoViewController (or FirstViewController) is the object you've designated as the delegate, since you've given the interface the "<ADBannerViewDelegate,demoViewControllerDelegate>" designations.
Then, from your Second View Controller, you can call the object you designated and set as a delegate by doing:
[delegate startSent];
and
[delegate stopSent];
in the appropriate locations, which appear to be "bannerViewActionShouldBegin" and "bannerViewActionDidFinish", respectively.
You should also make sure that the delegate is properly set, therefore instead of:
[delegate startSent];
you should actually do this:
if(delegate)
[delegate startSent];
else
NSLog( #"delegate is null; we should figure out why" );

Related

delegation and passing data back from childViewController

I have been struggling with this for a few days and have received valuable help on the way from S.O. I have made the simplest possible project to reduce the possibilities of it being a typo.
All my project is, is a ViewController that holds a container view hooked to a childViewController. The "parent" ViewController is set as the delegate of the childViewController. In the viewDidLoad of the child I am passing a value which is just a string. This string should be passed on to the parent and printed on the console. Here are the files.
ViewController.h
#import <UIKit/UIKit.h>
#import "ChildViewController.h"
#interface ViewController : UIViewController <ChildViewControllerDelegate>
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#property NSString *myValueRetrieved;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
ChildViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:#"ChildVC"];
controller.delegate = self;
NSLog(#"Here is my value: %#",self.myValueRetrieved);
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void) passValue:(NSString *)theValue{
self.myValueRetrieved = theValue;
}
#end
ChildViewController.h
#import <UIKit/UIKit.h>
#protocol ChildViewControllerDelegate;
#interface ChildViewController : UIViewController
#property (weak)id <ChildViewControllerDelegate> delegate;
#end
#protocol ChildViewControllerDelegate <NSObject>
- (void) passValue:(NSString*) theValue;
#end
ChildViewController.m
#import "ChildViewController.h"
#interface ChildViewController ()
#property NSArray *colors;
#end
#implementation ChildViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self.delegate passValue:#"Hello"];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
#end
Am I right to think that when the app is launched, the console should log the following message: "here is my value: hello". Am I doing something wrong in terms of logically not getting delegation or is it just a silly typo somewhere? tx
You're assuming that the view is loaded when the view controller is instantiated. That's now how it works. The view gets loaded when it's needed (like to add to the parent view).
But you can force the view to load and make this work. Call -loadViewIfNeeded on the child view controller right after setting the delegate. That will probably get you what you want:
controller.delegate = self;
[controller loadViewIfNeeded];
NSLog(#"Here is my value: %#",self.myValueRetrieved);
Or, if you do want to call back the delegate in viewDidLoad, then you'd need to move the NSLog to the -passValue: method, since the primary view controller's viewDidLoad method will have already finished running.
To do this make ParentController a delegate of ChildController. This allows ChildController to send a message back to ParentController enabling us to send data back.
For ParentController to be delegate of ChildController it must conform to ChildController's protocol which we have to specify. This tells ParentController which methods it must implement.
In ChildController.h, below the #import, but above #interface you specify the protocol.
#class ChildController;
#protocol ViewControllerBDelegate <NSObject>
- (void)addItemViewController:(ChildController *)controller didFinishEnteringItem:(NSString *)item;
#end
next still in the ChildController.h you need to setup a delegate property and synthesize in ChildController.h
#property (nonatomic, weak) id <ChildControllerDelegate> delegate;
In ChildController we call a message on the delegate when we pop the view controller.
NSString *itemToPassBack = #"Pass this value back to ParentController";
[self.delegate addItemViewController:self didFinishEnteringItem:itemToPassBack];
That's it for ChildController. Now in ParentController.h, tell ParentViewController to import Child and conform to its protocol.
import "ChildController.h"
#interface ParentController : UIViewController
In ParentController.m implement the following method from our protocol
- (void)addItemViewController:(ChildController *)controller didFinishEnteringItem:(NSString *)item
{
NSLog(#"This was returned from ChildController %#",item);
}
The last thing we need to do is tell ChildController that ParentController is its delegate before we push ChildController on to nav stack.
ChildController *ChildController = [[ChildController alloc] initWithNib:#"ChildController" bundle:nil];
ChildController.delegate = self
[[self navigationController] pushViewController:ChildController animated:YES];

delegate: no response, where is the secret?

I have a little trouble with my delegate example. I created a very simple code to learn how delegates work. I know that my delegate not will be called but i can't figure out why?
So here is the complete code. Please tell me what i do wrong. It is really important for me to understand the error in this code.
First Viewcontroller: h.file
#import <UIKit/UIKit.h>
#protocol ViewControllerDelegate;
#interface ViewController : UIViewController
#property (nonatomic, retain) id<ViewControllerDelegate> delegate;
#end
#protocol ViewControllerDelegate <NSObject>
- (void)transfer:(ViewController *)data number:(NSUInteger)value;
#end
First Viewcontroller: m.file
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize delegate;
- (void)viewDidLoad
{
[super viewDidLoad];
if ([delegate respondsToSelector:#selector(transfer:number:)]){
[delegate transfer:self number:65];
NSLog(#"delegate called");
}
[delegate transfer:self number:65]; //Try to call without if-statement.
}
#end
SecondViewcontroller: h.file
#import <UIKit/UIKit.h>
#import "ViewController.h"
#interface SecondViewController : UIViewController <ViewControllerDelegate>
#end
SecondViewcontroller: m.file
#import "SecondViewController.h"
#interface SecondViewController ()
#end
#implementation SecondViewController
- (void)transfer:(ViewController *)data number:(NSUInteger)value
{
NSLog(#"received information from ViewController");
}
- (void)viewDidLoad
{
[super viewDidLoad];
ViewController *viewcontroller = [[ViewController alloc] init];
viewcontroller.delegate = self;
}
#end
In the storyboard i use two container views so both ViewControllers will shown.
Your current code is fine. The problem appears to be that you are never loading the view controllers view(s) so the viewDidLoad method isn't being called.
To test, push the viewcontroller, or just request viewcontroller.view.

How to correctly implement a NSWindowController subclass with xib-file

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.

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.

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).