I have started working more with delegate as suggested in another question I made. Now, I have made a UIViewController called ProfileViewController in which I would like to load the News Feed from Facebook. I also have subclass of NSObject called FBFeed. This subclass loads the Facebook News Feed and should pass it back to the view controller. All my requests to Facebook are sent through a singleton named FBRequestWrapper which (in this case) should pass the result to FBFeed.
I call getFBRequestWithGraphPath:andDelegate: in FBRequestWrapper which will call a method in the Facebook iOS SDK which takes a delegate as a parameter. If I put in self (which will be the FBRequestWrapper) it works just fine. If I put in _delegate (which is an instance of FBFeed) the application crashes with EXC_BAD_ACCESS.
I have a theory why it might crash. I have read that
#property (nonatomic, retain) id <FBFeedDelegate> delegate;
should be
#property (nonatomic, assign) id <FBFeedDelegate> delegate;
But when doing so I get the following error:
Existing ivar 'delegate' for unsafe_unretained property 'delegate'
must be __unsafe_unretained
Also, I don't know if that enough to make the application crash.
Here is my code.
ProfileViewController.h
#import <UIKit/UIKit.h>
#import "FeedTableView.h"
#import "FBFeed.h"
#interface ProfileViewController : UIViewController <FBFeedDelegate>
{
FeedTableView *tableView;
}
- (void)loadFeed;
#end
ProfileViewController.m
#implementation ProfileViewController
- (void)loadFeed
{
FBFeed *feed = [[FBFeed alloc] init];
[feed loadNewsFeedWithDelegate:self];
}
#pragma mark -
#pragma FBFeedDelegate
- (void)finishedLoadingFeed:(FBFeed *)_feed
{
NSLog(#"ProfileViewController: FBFeed Finished.");
}
- (void)failedToLoadFeed:(FBFeed *)_feed
{
NSLog(#"ProfileViewController: FBFeed Failed.");
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Load feed
[self loadFeed];
}
#end
FBFeed.h
#import <Foundation/Foundation.h>
#import "FBRequestWrapper.h"
#protocol FBFeedDelegate;
#interface FBFeed : NSObject <FBRequestDelegate>
{
id <FBFeedDelegate> delegate;
}
#property (nonatomic, retain) id <FBFeedDelegate> delegate;
- (void)loadNewsFeedWithDelegate:(id)_delegate;
#end
#protocol FBFeedDelegate <NSObject>
#required
- (void)finishedLoadingFeed:(FBFeed *)_feed;
- (void)failedToLoadFeed:(FBFeed *)_feed;
#end
FBFeed.m
#import "FBFeed.h"
#implementation FBFeed
#synthesize delegate;
- (void)loadNewsFeedWithDelegate:(id)_delegate
{
self.delegate = _delegate;
[[FBRequestWrapper defaultManager] getFBRequestWithGraphPath:#"me" andDelegate:self];
}
#pragma mark -
#pragma mark FBRequest Delegate
- (void)request:(FBRequest *)request didLoad:(id)result
{
NSLog(#"FBFeed: FBRequest Did Load.");
NSLog(#"%#", result);
}
- (void)request:(FBRequest *)request didFailWithError:(NSError *)error
{
NSLog(#"FBFeed: FBRequest Failed.");
NSLog(#"%#", error);
}
#end
FBRequestWrapper.h
#import <Foundation/Foundation.h>
#import "FBConnect.h"
#interface FBRequestWrapper : NSObject <FBRequestDelegate, FBSessionDelegate>
{
Facebook *facebook;
BOOL isLoggedIn;
}
#property (nonatomic, assign) BOOL isLoggedIn;
+ (id)defaultManager;
- (void)setIsLoggedIn:(BOOL)_loggedIn;
- (void)FBSessionBegin:(id<FBSessionDelegate>)_delegate;
- (void)FBLogout;
- (void)getFBRequestWithGraphPath:(NSString *)_path andDelegate:(id)_delegate;
- (void)getFBRequestWithMethodName:(NSString *)_methodName andParams:(NSMutableDictionary *)_params andDelegate:(id)_delegate;
#end
FBRequestWrapper.m
#import "FBRequestWrapper.h"
static FBRequestWrapper *defaultWrapper = nil;
#implementation FBRequestWrapper
#synthesize isLoggedIn;
+ (id)defaultManager
{
if(!defaultWrapper)
{
defaultWrapper = [[FBRequestWrapper alloc] init];
}
return defaultWrapper;
}
- (void)getFBRequestWithGraphPath:(NSString *)_path andDelegate:(id)_delegate
{
if (_path != nil)
{
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
if (_delegate == nil)
{
_delegate = self;
}
[facebook requestWithGraphPath:_path andDelegate:_delegate];
}
}
#pragma mark -
#pragma mark FBRequestDelegate
- (void)request:(FBRequest *)request didLoad:(id)result
{
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
NSLog(#"%#", result);
}
- (void)request:(FBRequest *)request didFailWithError:(NSError *)error
{
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
NSLog(#"FBRequest Failed: %#", error);
}
#end
I guess you are using ARC and the new iOS5 SDK which is still under NDA. Instead of using retain, use the Keyword 'strong'. Using strong will have the same behaviour intended as using retain. Set up your property like this:
#property (nonatomic, strong) id <FBFeedDelegate> delegate;
I think there are two ways for you:
Don't use ARC and leave your code as it was
If you want to use ARC, be aware that delegates normally shouldn't be retained. But when using ARC, an ivar declaration like this:
id <FBFeedDelegate> delegate
defaults to
id <FBFeedDelegate> __strong delegate
So when you declare a property like this:
#property (readwrite, unsafe_unretained) id <FBFeedDelegate> delegate
The ivar should look like this:
id <FBFeedDelegate> __unsafe_unretained delegate
I had the same problem but then I was able to make it work correctly under iOS 5.1 on XCode 4.3.2
Use this code in your .h file
#protocol AnimationManagerCountdownTimerDelegate <NSObject>
#required
-(void)countdownCompleted;
#end
#interface AnimationManager : NSObject{
....
}
#property (nonatomic, strong) id <AnimationManagerCountdownTimerDelegate> countdownTimerDelegate;
#end
Then in the .m file you simply synthesize:
#synthesize countdownTimerDelegate;
Related
I used delegate mehod for pass data between view controllers. This is not working.
#protocol PassCountry <NSObject>
#required
- (void) setPickedCountry:(NSString *)pickedCountry;
#end
#interface SelectCountryViewController : UIViewController<UIPickerViewDelegate, UIPickerViewDataSource> {
id <PassCountry> delegate;
}
#property (copy) NSString *pickedCountry;
#property (retain) id delegate;
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent (NSInteger)component {
pickedCountry = [self.countries objectAtIndex:row];
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
return self.countries.count;
}
- (void)viewWillDisappear:(BOOL)animated {
[[self delegate] setPickedCountry:pickedCountry];
}
#import <UIKit/UIKit.h>
#protocol PassCountry <NSObject>
#required
- (void) setPickedCountry:(NSString *)pickedCountry;
#end
#interface secondViewViewController : UIViewController<UIPickerViewDelegate, UIPickerViewDataSource>
{
id <PassCountry> delegate;
IBOutlet UIButton *aButton;
}
#property (copy) NSString *pickedCountry;
#property (assign) id<PassCountry> delegate; // for delegate use assign don't retain
// in another class you are creating instance of this class
secondViewViewController *secController = [[secondViewViewController alloc]init];
secController.delegate = self;//check this added or not
[self presentViewController:secController animated:YES completion:nil];
//and implementation of deleagte method
- (void) setPickedCountry:(NSString *)pickedCountry
{
// do some stuff
}
Firstly, delegate instance can not be retained.
Secondly, delegate should be synthesized using "#synthesize delegate" before invoke the method [self delegate].
Try this::
.h File
#protocol delegateTextSize <NSObject>
#optional
-(void)selectedTextSize:(double)textSize;
#end
#interface CustomFontSizeCell : UITableViewCell
#property (nonatomic,retain) id delegateTextSize;
-(IBAction)changeSize:(id)sender;
#end
.m File
-(IBAction)changeSize:(id)sender
{
[delegateTextSize selectedTextSize:app.selectedFontSize];
}
Where to use,
.h File
Controller <delegateTextSize>
.m File
-(void)selectedTextSize:(double)textSize
{
}
Hopefully, this will work
Thanks.
I have some problem with my app and a protocol. I can't post image (...) but my Storyboard contains two controller. First is Main Menu controller with a UIButton. When the button is pressed by the user, a Table view appear with its controller "Available Server controller".
Well, The Main-Menu controller expose the following protocol:
#import <UIKit/UIKit.h>
#import "Client.h"
#import "MyRstAaAppDelegate.h"
#class MyRstAaClientViewController;
#protocol ClientNetworkDelegate <NSObject>
-(void)serverBecameAvaiable:(NSString *)peerID;
-(void)serverBecameUnavailable:(NSString *)peerID;
-(void)didConnectToServer:(NSString *)peerID;
-(void)didDisconnectFromServer:(NSString *)peerID;
#end
#interface MyRstAaClientViewController : UIViewController <ClientDelegate>
#pragma PROPERTIES
//Delegate property
#property (nonatomic, weak) id<ClientNetworkDelegate> delegate;
#end
And the Available Server Table view controller implements the protocol's methods to reload table data and to do other stuff. This is its code:
#import <UIKit/UIKit.h>
#import "MyRstAaClientViewController.h"
#interface MyRstAaClientServerListTableViewController : UITableViewController <ClientNetworkDelegate>
#end
...
...
And the .m
#import "MyRstAaClientServerListTableViewController.h"
#interface MyRstAaClientServerListTableViewController ()
//Client shared istance
#property (nonatomic, strong) Client *client;
#property (nonatomic, strong) MyRstAaClientViewController *mainController;
#end
#implementation MyRstAaClientServerListTableViewController
#pragma PROPERTIES
#synthesize client = _client;
#synthesize mainController = _mainController;
#pragma INITIALIZATION FUNCTIONS
- (void)viewDidLoad
{
[super viewDidLoad];
MyRstAaAppDelegate *appDelegate = (MyRstAaAppDelegate *)[[UIApplication sharedApplication]delegate];
self.client = appDelegate.client;
if(_mainController == nil)
_mainController = [[MyRstAaClientViewController alloc]init];
[self.mainController setDelegate:self];
[self.tableView reloadData];
}
#pragma DELEGATES
-(void)serverBecameAvaiable:(NSString *)peerID;
{
//Reload table data
[self.tableView reloadData];
#ifdef DEBUG
NSLog(#"CLIENT-SERVERLIST:: Server become Available");
#endif
}
-(void)serverBecameUnavailable:(NSString *)peerID;
{
//Reload table data
[self.tableView reloadData];
#ifdef DEBUG
NSLog(#"CLIENT-SERVERLIST:: Server become unavaiable");
#endif
}
- (void)didConnectToServer:(NSString *)peerID
{
#ifdef DEBUG
NSLog(#"CLIENT-SERVERLIST:: Server become Available");
#endif
}
- (void)didDisconnectFromServer:(NSString *)peerID
{
#ifdef DEBUG
NSLog(#"CLIENT-SERVERLIST:: did Disconnect From Server");
#endif
}
This is an example of delegate call on the .m file of MainMenu controller of my app
#import "MyRstAaClientViewController.h"
#interface MyRstAaClientViewController ()
#property (nonatomic, strong) Client *client;
#end
#implementation MyRstAaClientViewController
#pragma PROPERTIES
#synthesize client = _client;
#synthesize delegate = _delegate;
-(void)Client:(Client *)client serverBecameUnavailable:(NSString *)peerID
{
#ifdef DEBUG
NSLog(#"CLIENT-HOME:: Server become unavaiable");
#endif
//Call delegate methodå
[self.delegate serverBecameUnavailable:peerID];
}
The problem is that when I run my app on my iPhone, and I go under "Available Server" list view, I can see, for example, on the output the string: CLIENT-HOME:: Server become unavaiable but the corresponding delegate function in the Table view controller is never called. Why??
There's something wrong in use of delegate and protocol pattern?
Scenario: Need to execute CustomWebview delegate on view controller.
Please help me with this code. Instead of using callback, I need to use "Protocol". Can it be done or we can only use callback in this scenario.
On ViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
//MyWebView *webView = [[MyWebView alloc] initWithDelegate:self callback:#selector(finishLoading)];
MyWebView *webView= [[MyWebView alloc] initWithFrame:CGRectMake(0,0,320, 460)];
[webView LoadURL:#"http://192.168.5.165/"];
[webView setDelegate:self];
[webView setCallback:#selector(finishLoading)];
[self.view addSubview:webView] ;
}
- (void) finishLoading
{
NSLog(#"Finish");
}
On MyWebView.h
#interface MyWebView : UIView<UIWebViewDelegate> {
NSString *strURL;
}
#property(nonatomic, retain) NSString *strURL;
#property (nonatomic, assign) id delegate;
#property (nonatomic, assign) SEL callback;
-(void) LoadURL:(NSString*)url;
#end
On MyWebView.m
#import "MyWebView.h"
#implementation MyWebView
#synthesize strURL,delegate,callback;
UIWebView *webView;
-(id) initWithFrame:(CGRect)frame
{
if(self =[super initWithFrame:frame])
{
webView = [[UIWebView alloc] initWithFrame:frame];
webView.delegate = self;
[self addSubview:webView];
}
return self;
}
-(void) LoadURL:(NSString*)url
{
NSURL *u = [NSURL URLWithString:url];
NSURLRequest *req= [NSURLRequest requestWithURL:u];
[webView loadRequest:req];
}
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
[delegate performSelector:callback];
}
Your question is a bit unclear. A protocol and a delegate are two entirely separate, though related, things--apples and oranges. A protocol defines a list of methods an object may or must respond to:
#protocol Document
+ (id)documentWithContentsOfURL: (NSURL *)aURL;
- (void)writeToURL: (NSURL *)aURL error: (NSError **)outError;
#end
A delegate is an object, usually an instance of a custom class, that is handed to another object for custom processing or feedback--that latter object delegates work to the delegate object.
Are you asking how to convert a delegate category on NSObject to a delegate protocol? (The former used to be Apple's pattern for defining the obligations and abilities of a delegate; the latter is the newer way to do the same thing.) If so, it generally looks something like this:
Delegate Category on NSObject
#interface NSObject (WidgetDelegate)
- (void)doSomethingWithWidget: (Widget *)w;
#end
#interface Widget : NSObject
#property (readwrite, assign) id delegate;
#end
Delegate Protocol
#protocol WidgetDelegate <NSObject>
- (void)doSomethingWithWidget: (Widget *)w;
#end
#interface Widget : NSObject
#property (readwrite, assign) id <WidgetDelegate> delegate;
#end
Is that what you're looking for? If not, can you clarify exactly what you're trying to do?
Also see Apple's Communicating with Objects, which discusses delegates, protocols, and selectors. Though its listed under Mac OS X, most (if not all) appears to apply to iOS also.
On ViewController.m
#interface MyViewController : UIViewController<MyFinishLoadingDelegate> {
//your own definition
}
- (void)viewDidLoad
{
[super viewDidLoad];
//MyWebView *webView = [[MyWebView alloc] initWithDelegate:self callback:#selector(finishLoading)];
MyWebView *webView= [[MyWebView alloc] initWithFrame:CGRectMake(0,0,320, 460)];
[webView LoadURL:#"http://192.168.5.165/"];
[webView setDelegate:self];
[self.view addSubview:webView] ;
}
- (void)finishLoading //this is your implementation for MyFinishLoadingDelegate
{
NSLog(#"Finish");
}
On MyWebView.h
//you declare the protocol here to tell anyone who'd like to act like your delegate that they need to implement "finishLoading"
#protocol MyFinishLoadingDelegate <NSObject>
- (void)finishLoading;
#end
#interface MyWebView : UIView<UIWebViewDelegate> {
NSString *strURL;
}
#property(nonatomic, retain) NSString *strURL;
#property (nonatomic, assign) id<MyFinishLoadingDelegate> delegate;
-(void) LoadURL:(NSString*)url;
#end
On MyWebView.m
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
[delegate finishLoading];
}
I have a simple project to present a modal view controller and transfer back a string based on which button in the modal VC that gets pressed. I based it all on watching the Stanford class on iTunes U. It looks like I have everything correct, but I get a couple of compiler warnings.
First I get one called passing argument 1 of 'setDelegate:' from incompatible pointer type in TransferViewController.m
Second I get four warnings called Invalid receiver type 'id <MyModalViewControllerDelegate>*' but these aren't displayed in the build results area, rather next to the offending lines in MyModalViewController.m, both lines in each of the button actions.
Here's the code...
// TransferViewController.h
#import <UIKit/UIKit.h>
#import "MyModalViewController.h";
#interface TransferViewController : UIViewController <MyModalViewControllerDelegate> {
UILabel *label;
UIButton *button;
}
#property (nonatomic, retain) IBOutlet UILabel *label;
#property (nonatomic, retain) UIButton *button;
- (IBAction)updateText;
#end
// TransferViewController.m
#import "TransferViewController.h"
#implementation TransferViewController
#synthesize label;
#synthesize button;
- (IBAction)updateText {
MyModalViewController *myModalViewController = [[MyModalViewController alloc] init];
myModalViewController.delegate = self; // I get the warning here.
[self presentModalViewController:myModalViewController animated:YES];
[myModalViewController release];
}
- (void)myModalViewController:(MyModalViewController *)controller didFinishSelecting:(NSString *)selectedDog {
label.text = selectedDog;
[self dismissModalViewControllerAnimated:YES];
}
#end
// MyModalViewController.h
#import <UIKit/UIKit.h>
#protocol MyModalViewControllerDelegate;
#interface MyModalViewController : UIViewController {
UIButton *abby;
UIButton *zion;
id <MyModalViewControllerDelegate> delegate;
}
#property (assign) id <MyModalViewControllerDelegate> delegate;
- (IBAction)selectedAbby;
- (IBAction)selectedZion;
#end
#protocol MyModalViewControllerDelegate <NSObject>
#optional
- (void)myModalViewController:(MyModalViewController *)controller didFinishSelecting:(NSString *)selectedDog;
#end
// MyModalViewController.m
#import "MyModalViewController.h"
#implementation MyModalViewController
#synthesize delegate;
- (IBAction)selectedAbby {
if ([self.delegate respondsToSelector:#selector (myModalViewController:didFinishSelecting:)]) {
[self.delegate myModalViewController:self didFinishSelecting:#"Abby"];
}
}
- (IBAction)selectedZion {
if ([self.delegate respondsToSelector:#selector (myModalViewController:didFinishSelecting:)]) {
[self.delegate myModalViewController:self didFinishSelecting:#"Zion"];
}
}
Get rid of those *s after id <something> and before delegate.
So make this
id <MyModalViewControllerDelegate> *delegate;
this
id <MyModalViewControllerDelegate> delegate;
I have an UIViewController named MainViewController
I have another UIViewController named LeftSharingViewController;
I would like to get and use the NSString from MainViewController in my LeftSharingViewController
I have a problem, I always get (null) instead of the NSString wanted value.
Here's my code and how does the NSString get it's value
MainViewController:
- (void)webViewDidFinishLoad:(UIWebView *)webView {
leftWebViewString = [NSString stringWithString:leftWebView.request.URL.absoluteString];
}
LeftSharingViewController.h:
#import <UIKit/UIKit.h>
#import "MainViewController.h"
#import <MessageUI/MessageUI.h>
#import <MessageUI/MFMailComposeViewController.h>
#class MainViewController;
#interface LeftSharingViewController : UIViewController <MFMailComposeViewControllerDelegate> {
MainViewController *mainViewController;
NSString *leftWebViewUrl;
}
#property (nonatomic, retain) MainViewController *mainViewController;
#property (nonatomic, retain) NSString *leftWebViewUrl;
#end
LeftSharingViewController.m:
#import "LeftSharingViewController.h"
#import "MainViewController.h"
#implementation LeftSharingViewController
#synthesize mainViewController;
#synthesize leftWebViewUrl;
- (void)viewWillAppear:(BOOL)animated {
self.leftWebViewUrl = self.mainViewController.leftWebViewString;
}
#pragma mark -
#pragma mark Compose Mail
-(void)displayComposerSheet
{
MFMailComposeViewController *mailPicker = [[MFMailComposeViewController alloc] init];
mailPicker.mailComposeDelegate = self;
[mailPicker setSubject:#"Check Out This Website!"];
[mailPicker setMessageBody:[NSString stringWithFormat:#"Take a look at this site:%#", leftWebViewUrl] isHTML:YES];
mailPicker.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentModalViewController:mailPicker animated:YES];
[mailPicker release];
}
Thanks!
On your MainViewController, you are never retaining the NSString you create. You are using stringWithString:, which returns an autoreleased object.
You should either retain it, or (easier) change your code to use your accessor, which already retains, like this:
- (void)webViewDidFinishLoad:(UIWebView *)webView {
self.leftWebViewString = [NSString stringWithString:leftWebView.request.URL.absoluteString];
}
As a side note, if you are navigating from MainViewController to LeftSharingViewController, instead of "pulling" the data in LeftSharingViewController from MainViewController, I would do it the other way around:
In MainViewController, before displaying the view of LeftSharingViewController, I'd set the required properties.