I am new to Objective C and trying to grasp the concept of a delegate. I have the following code and have made comments where a warning and an error occur with the build.I am using the latest version of Xcode and using a Storyboard.
From MainViewController.h
#import <UIKit/UIKit.h>
#import "FinalViewController.h"
#class MainViewController;
#protocol MainViewControllerDelegate <NSObject>
- (void)sayHello;
#end
#interface MainViewController : UIViewController
#property (nonatomic, strong) id <MainViewControllerDelegate> delegate;
- (void)writeToMe;
. . .
#end
From MainViewController.m
#import "MainViewController.h"
#import "FinalViewController.h"
#interface MainViewController ()
#end
#implementation MainViewController
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)writeToMe
{
[self.delegate sayHello];
}
. . .
#end
From FinalViewController.h
#import <UIKit/UIKit.h>
#import "MainViewController.h"
#interface FinalViewController : UIViewController <NSCoding, MainViewController> //The ERROR below appears
//ERROR:Cannot find protocol declaration for ‘MainViewControllerDelegate’; did you mean ‘UIPageViewControllerDelegate’?
. . .
#end
From FinalViewController.m
#import "FinalViewController.h"
#interface FinalViewController ()
#end
#implementation FinalViewController
- (void)viewDidLoad
{
[super viewDidLoad];
MainViewController *mainVC;
mainVC.delegate = self; //causes WARNING below
//WARNING:Assigning to ‘id <MainViewControllerDelegate> from incompatible type ‘FinalViewController *const__strong’
[mainVC writeToMe];
. . .
}
- (void)sayHello
{
NSLog(#"sayHello called");
}
#end
I have researched problems and responses to a similar problem on this web site, but have not found a solution using Storyboard.
Change:
#import "MainViewController.h"
#import "FinalViewController.h"
#interface MainViewController ()
#end
#implementation MainViewController
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)writeToMe
{
[self.delegate sayHello];
}
. . .
TO:
#import "MainViewController.h"
#interface MainViewController ()
#end
#implementation MainViewController
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)writeToMe
{
[self.delegate sayHello];
}
. . .
ALSO:
#import <UIKit/UIKit.h>
#import "FinalViewController.h"
#class MainViewController;
#protocol MainViewControllerDelegate <NSObject>
- (void)sayHello;
#end
#interface MainViewController : UIViewController
#property (nonatomic, strong) id <MainViewControllerDelegate> delegate;
- (void)writeToMe;
. . .
#end
TO
#import <UIKit/UIKit.h>
#protocol MainViewControllerDelegate <NSObject>
- (void)sayHello;
#end
#interface MainViewController : UIViewController
#property (nonatomic, weak) id <MainViewControllerDelegate> delegate;
- (void)writeToMe;
. . .
#end
--------------------------- EDIT ------------------------------------
Change
#interface FinalViewController : UIViewController <NSCoding, MainViewController>
to
#interface FinalViewController : UIViewController <NSCoding, MainViewControllerDelegate>
This might be a typo but:
#interface FinalViewController : UIViewController <NSCoding, MainViewController>
should be:
#interface FinalViewController : UIViewController <NSCoding, MainViewControllerDelegate>
Also remove the import of "FinalViewController.h" from MainViewController.h.
And your other warning can be fixed by changing:
#interface FinalViewController ()
to:
#interface FinalViewController () < MFMailComposeViewControllerDelegate>
and change:
mainVC.delegate = self;
to:
mainVC.mailComposeDelegate = self;
Related
I am writing a sample code in order to understand message forwarding in Objective C (iOS).
I have two classes (class A and class B). I want to create an instance of class B and set a class A instance variable to it. I am calling the Forward Invocation method (Message Forwarding) in the following code.
// ViewController.h
// TestInvocation
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#end
// ViewController.m
// TestInvocation
#import "ViewController.h"
#import "TestInvocation.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[TestInvocation testRun];
[super viewDidLoad];
}
#end
// TestInvocation.h
// TestInvocation
#import <Foundation/Foundation.h>
#interface TestInvocation : NSObject
{}
+(void)testRun;
#end
// TestInvocation.m
// TestInvocation
#import "TestInvocation.h"
#import "ClassB.h"
#import "ClassA.h"
#implementation TestInvocation
+(void)testRun
{
ClassB* diplomat = [[ClassB alloc] init];
NSLog(#"value = %d",[diplomat value]);// Error shows up here on running:
//No visible #interface for 'ClassB' declares the selector 'value'
}
#end
// ClassA.h
// TestInvocation
#import <Foundation/Foundation.h>
#interface ClassA : NSObject
{
int value;
}
#property(readwrite,assign) int value;
#end
// ClassA.m
// TestInvocation
#import "ClassA.h"
#implementation ClassA
#synthesize value;
#end
// ClassB.h
// TestInvocation
#import <Foundation/Foundation.h>
#interface ClassB : NSObject
{}
#end
// ClassB.m
// TestInvocation
#import "ClassB.h"
#import "ClassA.h"
#implementation ClassB
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
return [ClassA instanceMethodSignatureForSelector:aSelector];
}
-(void)forwardInvocation:(NSInvocation *)anInvocation
{
ClassA* negotiate = [[ClassA alloc] init];
negotiate.value = 15;
[anInvocation invokeWithTarget:negotiate];
}
#end
I am expecting the above code to work. But instead I get the following build time error:
ARC Semantic Issue
TestInvocation.m:19:35: No visible #interface for 'ClassB' declares the selector 'value'
Class B should have the property in interface at least.
But you can make it #dynamic in implementation if you want to call forwardInvocation.
#interface ClassB : NSObject
#property (readwrite, assign) int value;
#end
#implementation ClassB
#dynamic value;
...
#end
I think it should work for you.
When I tried having protocols in below two classes, compiler says that the protocol declarations cannot be found
ViewController:
#import <UIKit/UIKit.h>
#import "SecondViewController.h"
#protocol FlipOtherSideViewControllerDelegate;
#interface ViewController : UIViewController<FlipsideViewControllerDelegate> {
id <FlipOtherSideViewControllerDelegate> __unsafe_unretained delegate;
}
- (IBAction)switchMode:(id)sender;
#property (unsafe_unretained) id <FlipOtherSideViewControllerDelegate> delegate;
#end
#protocol FlipOtherSideViewControllerDelegate
- (void)flipothersideViewControllerDidFinish:(ViewController *)controller;
#end
SecondViewController:
#import <UIKit/UIKit.h>
#import "ViewController.h"
#protocol FlipsideViewControllerDelegate;
#interface SecondViewController : UIViewController <FlipOtherSideViewControllerDelegate> {
id <FlipsideViewControllerDelegate> __unsafe_unretained delegate;
}
#property (unsafe_unretained) id <FlipsideViewControllerDelegate> delegate;
#end
#protocol FlipsideViewControllerDelegate
- (void)flipsideViewControllerDidFinish:(SecondViewController *)controller;
#end
Any suggestion on above?
Thanks in advance.
Why you declaring below the interface? Try to declare just before the interface declaration. I think there should not be any error then.
Since I switched my iOS project to ARC, I keep getting this compiler error:
No visible #interface for 'CDViewController' declares the selector
'setUseAgof:'
In this line:
[self.viewController setUseAgof:false];
In this file:
AppDelegate.m
#import "AppDelegate.h"
#import "MainViewController.h"
#import <Cordova/CDVPlugin.h>
#import "NSString+MD5.h"
#implementation AppDelegate
#synthesize window, viewController;
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
// (...)
[self.viewController setUseAgof:false]; // <-- HERE
// (...)
return YES;
}
#end
Although the method is definitely defined:
MainViewController.h
#import <Cordova/CDVViewController.h>
#import <ADTECHMobileSDK/ADTECHMobileSDK.h>
#interface MainViewController : CDVViewController <ATInterstitialViewDelegate>{
ATInterstitialView *interstitial;
}
- (void)setUseAgof:(BOOL)useAgofParam;
#end
#interface MainCommandDelegate : CDVCommandDelegateImpl
#end
#interface MainCommandQueue : CDVCommandQueue
#end
MainViewController.m
#import "MainViewController.h"
#interface MainViewController()
- (void)setUseAgof:(BOOL)useAgofParam;
#end
#implementation MainViewController
BOOL useAgof = true;
- (void)setUseAgof:(BOOL)useAgofParam
{
NSLog(#"1.) Setting useAgof = %d", (int)useAgofParam);
useAgof = useAgofParam;
}
I don't get it. What's wrong?
Update:
AppDelegate.h
#import <UIKit/UIKit.h>
#import <Cordova/CDVViewController.h>
#import <INFOnlineLibrary/INFOnlineLibrary.h>
#interface AppDelegate : NSObject <UIApplicationDelegate>{}
#property (nonatomic, strong) IBOutlet UIWindow* window;
#property (nonatomic, strong) IBOutlet CDVViewController* viewController;
#end
viewController seems to be declared as pointer of CDVViewController. You are calling a method which is part of MainViewController the class derived from CDVViewController. Method being called is part of derived class and not the base class, hence make viewController pointer of MainViewController instead.
Make sure the viewController property in your AppDelegate has a type of CDVViewController * rather than UIViewController *.
So I have 3 views as follows: viewController >> viewController2 >> viewController3.
In viewController3 I have created a Delegate Protocol. The protocol method is a simple method that prints out an NSLog.
When I call the delegates from ViewController3, only its parent (viewController2 ) responds not the (first) viewController. There are no errors.I think problem has got something to do with [v2 setDelegate:self]; in the viewController.m file.
Nevertheless,[self.v3 setDelegate:self]; works fine in ViewController2.m file.
Why does the (first) viewController delegate not respond ? Do delegates only work with its immediate child ??
> **ViewController.h**
#import <UIKit/UIKit.h>
#import "ViewController2.h"
#import "ViewController2.h"
#interface ViewController : UIViewController <PassData>{
ViewController2 *v2;
}
#property (strong, nonatomic) ViewController2 *v2;
> Blockquote
- (IBAction)button:(id)sender;
#end
> **ViewController.M**
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize v2;
- (IBAction)button:(id)sender {
v2 = [[ViewController2 alloc]initWithNibName:#"ViewController2" bundle:nil];
[v2 setDelegate:self];
[self.view addSubview:v2.view];
}
-(void)print: (BOOL)success;{
if (success == YES) {
NSLog(#"ViewController called");
}
}
#end
> > ViewController2.h
#import <UIKit/UIKit.h>
#import "ViewController3.h"
#interface ViewController2 : UIViewController <PassData> {
ViewController3 *v3;
}
#property (strong, nonatomic)ViewController3 *v3;
#property (retain) id delegate;
- (IBAction)button:(id)sender;
#end
ViewController2.m
#import "ViewController2.h"
#interface ViewController2 ()
#end
#implementation ViewController2
#synthesize v3,delegate;
- (IBAction)button:(id)sender {
v3 = [[ViewController3 alloc]initWithNibName:#"ViewController3" bundle:nil];
[self.v3 setDelegate:self];
[self.view addSubview:v3.view];
}
-(void)print: (BOOL)success;{
if (success == YES) {
NSLog(#"ViewController2 called");
}
}
#end
> ViewController3.h
#import <UIKit/UIKit.h>
#protocol PassData <NSObject>
#required
-(void)print:(BOOL)success;
#end
#interface ViewController3 : UIViewController {
id<PassData> delegate;
}
#property (retain) id delegate;
- (IBAction)callButton:(id)sender;
#end
ViewController3.m
#import "ViewController3.h"
#interface ViewController3 ()
#end
#implementation ViewController3
#synthesize delegate;
- (IBAction)callButton:(id)sender {
// call all delegates
[[self delegate]print:YES];
}
#end
v2 doesn't have a method "print", that's a protocol method of v3 -- you can't chain delegate messages like this. If you want multiple controllers to respond to something in another controller, then you should use an NSNotification -- any number of objects can register to receive a notification.
I was playing around with MKMap and some custom delegate it's not working here and i really don't know why :/
Here's my code :
LocationViewController.h
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#protocol LocationViewDelegate <NSObject>
#required
- (void)didReceiveLocation:(CLLocation *)location;
#end
#interface LocationViewController : UIViewController <CLLocationManagerDelegate>
#property (strong, nonatomic) IBOutlet UILabel *locationLabel;
#property (nonatomic, strong) id<LocationViewDelegate> delegate;
- (IBAction)sendLocation:(id)sender;
#end
LocationViewController.m
[...]
- (IBAction)sendLocation:(id)sender {
if ([_delegate respondsToSelector:#selector(didReceiveLocation:)]) {
[_delegate didReceiveLocation:_currentLocation];
} else {
NSLog(#"nope");
}
}
[...]
MapViewController.h
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#import "LocationViewController.h"
#interface MapViewController : UIViewController <LocationViewDelegate>
#end
MapViewController.m
[...]
- (void)didReceiveLocation:(CLLocation *)location {
_mapView.centerCoordinate = location.coordinate;
}
[...]
I'm always getting the "nope" message means that respondsToSelector returns NO.
I did pretty the same example a few days ago and everything was fine.
Someone can see where's the problem here ?
Set your MapViewController as the delegate for the LocationViewController i.e. in your MapViewController.m:
self.locationViewController.delegate = self;
Just as an aside, I'm presuming your MapViewController owns a LocationViewController instance, if so it's better to set the delegate property in LocationViewController to 'weak' to avoid a circular reference. #property (nonatomic, weak) id delegate;