I have looked at all the other questions with the same problem but I cannot seem to get my heard around it. I am pretty sure I have done everything correctly as this is not my first time using delegates.
//PDFView.h
#class PDFView;
#protocol PDFViewDelegate <NSObject>
-(void)trialwithPOints:(PDFView*)pdfview;
#end
#interface PDFView : UIView
#property (nonatomic, weak) id <PDFViewDelegate> delegate;
In the implementation file i am trying to call the delegate method from touchesMoved delegate of view
//PDFView.m
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
[self.delegate trialwithPOints:self];
}
The class where the delegate method is implemented
//points.h
#import "PDFView.h"
#interface points : NSObject <PDFViewDelegate>
//points.m
//this is where the delegate is set
- (id)init
{
if ((self = [super init]))
{
pdfView = [[PDFView alloc]init];
pdfView.delegate = self;
}
return self;
}
-(void)trialwithPOints:(PDFView *)pdf
{
NSLog(#"THE DELEGATE METHOD CALLED TO PASS THE POINTS TO THE CLIENT");
}
So this is how i have written my delegate and somehow the delegate is nil and the delegate method is never called.
At the moment I am not doing anything with the delegate, I just want to see it working.
Any advices on this would be highly appreciated.
I think it is because you did not hold the reference to the instance of the delegate, and it got released because it is declared weak. You might be doing this:
pdfView.delegate = [[points alloc] init];
which you should fix to something like:
_points = [[points alloc] init];
pdfView.delegate = _points;
where _points is instance variable.
Related
I am trying to update the contents of an NSTextView that is connected to myViewController as a referencing outlet to the Files Owner which is the subclass myViewController.
When I use an IBAction from a button, or use the viewDidLoad method of the controller, I can update the text fine. However, when I try run the method from another class (referred to in this example as anotherViewController), it runs the method, but the textview does not change.
myViewController.h:
#import <Cocoa/Cocoa.h>
#import "anotherViewController.h"
#interface myViewController : NSViewController { }
#property (unsafe_unretained) IBOutlet NSTextView *outText;
#property (weak) IBOutlet NSButton *updateMeButton;
- (void)updateTextView:(NSString *)argText;
- (void)updateTextViewWithoutArg;
#end
myViewController.m:
#import "myViewController.h"
#interface myViewController ()
#end
#implementation myViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.outText.string = #"I work successfully";
}
- (IBAction)updateMeButton:(id)sender {
self.outText.string = #"I am updated text! I also work!";
}
- (void)updateTextView:(NSString *)argText {
self.outText.string = #"I don't make it to the NSTextView :(";
NSLog(#"Should have updated text view");
}
- (void)updateTextViewWithoutArg {
self.outText.string = #"I don't make it to the NSTextView :(";
NSLog(#"Should have updated text view");
}
#end
In anotherViewController.m , which has all the relevant imports, I call this:
myViewController *viewtask = [[myViewController alloc] init];
[viewtask updateTextViewWithoutArg];
Nothing happens. The method runs and logs that it should have updated, but no text updates. I have tried many different approaches, including textstorage and scrollrange methods, they all work the already working sections, but make no difference in the sections not working.
I've also tried just for fun:
myViewController *viewtask;
[viewtask updateTextViewWithoutArg];
Also using the instance variable _outText
Also using [self.outText setString:#"string"];
Also using [_outText setString:#"string"];
Again, they work but only in the already working sections.
This should be simple but isn't logical to me. In swift all I need to do is
self.outText.string = "I update whenever I'm called!"
Views you create in Interface Builder are lazily created, so if you access them before viewDidLoad is called they are nil.
If your case, calling
myViewController *viewtask = [[myViewController alloc] init];
does not cause the views to be created so when you call
[viewtask updateTextViewWithoutArg];
self.outText is nil.
You can see that this is what is happening by updating your code as below:
- (void)updateTextView:(NSString *)argText {
NSAssert(self.outText != nil, #"self.outText must not be nil");
self.outText.string = #"I don't make it to the NSTextView :(";
NSLog(#"Should have updated text view");
}
you should see the assert fire.
I appear to have found a solution by making myViewController a singleton class and using sharedInstance. For this particlar app, myViewController is a debug output window and will never need to be placed in another view.
I won't accept this answer yet, as it's not the best one I'm sure. There may still be a proper solution presented that allows finding the applicable myViewController instance, and modifying the outText property attached to it. Using this singleton makes subclassing tedious as I would have to make a new class for every instance if I wanted to be able to address say 10 View Controllers.
Anyway - the way I've been able to satisfy my simple requirement:
myViewController.h:
#import <Cocoa/Cocoa.h>
#import "anotherViewController.h"
#interface myViewController : NSViewController { }
#property (unsafe_unretained) IBOutlet NSTextView *outText;
#property (weak) IBOutlet NSButton *updateMeButton;
- (void)updateTextView:(NSString *)argText;
- (void)updateTextViewWithoutArg;
+ (id)sharedInstance;
#end
myViewController.m:
#import "myViewController.h"
#interface myViewController ()
#end
#implementation myViewController
static myViewController *sharedInstance = nil;
+ (myViewController *)sharedInstance {
static myViewController *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[myViewController alloc] init];
});
return sharedInstance;
}
- (void)viewDidLoad {
[super viewDidLoad];
sharedInstance = self;
}
- (void)viewDidUnload {
sharedInstance = nil;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.outText.string = #"I work successfully";
}
- (IBAction)updateMeButton:(id)sender {
sharedInstance.outText.string = #"Button Pressed";
}
- (void)updateTextView:(NSString *)argText {
sharedInstance.outText.string = argText;
}
- (void)updateTextViewWithoutArg {
sharedInstance.outText.string = #"I make it to the TextView now";
}
#end
Now when I use this code from within anotherViewController.m it updates the right instance:
[myViewController.sharedInstance updateTextView:#"Updating with this string"];
I'm trying to implement delegation. In the .h file of a custom class , I do this
#import <UIKit/UIKit.h>
#class Timer;
#protocol TimerDelegate
-(void)myClassDelegateMethod:(Timer *)timer;
#end
typedef void(^MyCustomBlock)(void);
#interface Timer : UILabel
#property (nonatomic, weak) id <TimerDelegate> delegate;
In the .m file I synthesize the delegate and also called the delegate method, checking to see first if the delegate implements the method
#synthesize delegate;
-(void)countdownTime:(NSTimer *)timer
{
NSLog(#"countdownTime called");
....
[self.delegate myClassDelegateMethod:self];
if (self.delegate != nil && [self.delegate respondsToSelector:#selector(myClassDelegateMethod:)]) {
[self.delegate performSelector:#selector(myClassDelegateMethod:)];
} else {
NSLog(#"Delegate doesn't implement myClassDelegateMethod");
}
when I run my code, I'm told the delegate doesn't implement the method. Here's how I implement it
In the viewController, I declare that it conforms to the protocol
#interface scViewController : UIViewController <TimerDelegate>
And then in the .m file of the viewController, I implement the delegate's method
- (void) myClassDelegateMethod:(Timer *) sender {
NSLog(#"Delegates are great!");
}
Can you explain how I've failed to implement the delegate method properly?
Update, in the viewController, I have a method that creates timer instances
-(Timer *)timer
{
_timer = [[Timer alloc] init];
return _timer;
}
In viewDidLoad, I do this
self.timer.delegate = self;
Your timer method is a problem. It should be:
-(Timer *)timer
{
if (!_timer) {
_timer = [[Timer alloc] init];
}
return _timer;
}
As you have it, every time you do self.timer you were creating a new timer so the delegate was only applied to one of the many instances.
My approach to this may be all wrong so I appreciate your patience.
I have a button in my main XIB file linked to this method in my document.m file:
- (IBAction)showTagModal:(id)sender {
if (!_FileTagWindowController){
_FileTagWindowController = [[FileTagWindowController alloc]init];
}
[_FileTagWindowController showWindow:self];
}
_FileTagWindowController is declared as a property in document.h and using breakpoints when the method is called, as far as I can tell is initializing properly, however _windowNibName and _window remains nil.
FileTagWindowController.h looks like this.
#import <Cocoa/Cocoa.h>
#interface FileTagWindowController : NSWindowController{
}
#property (strong) IBOutlet NSArrayController *tagsArray;
- (IBAction)saveContext:(id)sender;
#end
FileTagWindowController.m looks like this:
#import "FileTagWindowController.h"
#interface FileTagWindowController ()
#end
#implementation FileTagWindowController
- (id)initWithWindow:(NSWindow *)window
{
self = [super initWithWindow:window];
if (self) {
// Initialization code here.
}
return self;
}
- (void)windowDidLoad
{
[super windowDidLoad];
NSLog(#"Window Did Load!");
// Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
}
- (IBAction)saveContext:(id)sender {
}
#end
in my FileTagWindowController.xib I have File Owner set to FileTagWindowController as the custom class. I have the File Owner's "window" outlet linked to the window (NSPanel). That's all that should be required correct? The NSLOG statement in WindowDidLoad never gets called. I tried using [super initWithWindowNibName] in FileTagWindowController.m but that crashes not only the app, but Xcode as well with an endless initialization loop. Am I missing something obvious here?
Thanks all so much.
Try something like the following.
// document.h
#import "FileTagWindowController.h"
#property (strong) filetagWindowController *FileTagWindowController;
// document.m
#synthesize filetagWindowController;
- (IBAction)showTagModal:(id)sender {
if (self.filetagWindowController == nil) {
self.filetagWindowController = [[FileTagWindowController alloc] initWithWindowNibName:#"FileTagWindowController"];
}
[filetagWindowController showWindow:self];
[[filetagWindowController window] setReleasedWhenClosed:NO];
[NSApp runModalForWindow:filetagWindowController.window];
filetagWindowController = nil;
}
You may also want to call NSWindowWillCloseNotification to observe its state and see if filetagWindowController is closed.
I'm completely stuck with calling a method from a UIView subclass, the method just doesn't get fired, I have a feeling that I'm doing something wrong but after searching the web I did not find any clue. Thank you in advance
Here's the iPadMainViewController.h file
#import <UIKit/UIKit.h>
#import "TouchView.h"
#interface iPadMainViewController : UIViewController <TouchViewDelegate>
#property (retain) UIWebView *detailsView;
#end
and the iPadMainViewController.h file that holds the method
- (void)MethodNameToCallBack:(NSString *)s
{
NSLog(#"%#",s);
}
Here's the TouchView.h file, which is supposed t
#protocol TouchViewDelegate
- (void)MethodNameToCallBack:(NSString *)s;
#end
#interface TouchView : UIView {
id<TouchViewDelegate> delegate;
}
#property (nonatomic, assign) id delegate;
#end
Here's the TouchView.m file which is supposed to call a method of it's delegate
#implementation TouchView
#synthesize delegate;
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"HELLO FROM INSIDE");
[[self delegate] MethodNameToCallBack:(NSString *)#"HELLO FROM OUTSIDE"];
}
#end
Synthesizing the delegate is not enough, because it just creates the getter and the setter methods. It does not create an instance of iPadMainViewController.
So after you create an instance of TouchView, you should assign an instance of iPadMainViewController as the delegate.
iPadMainViewController *controller = [[iPadMainViewController alloc] init...
// ...
TouchView *touchView = [[TouchView alloc] init...
// ...
touchView.delegate = controller;
Or in the iPadMainViewController's viewDidLoad method:
- (void)viewDidLoad
{
// ...
self.touchView.delegate = self;
}
check after you instantiated a TouchView instance, did you assign its delegate?
Enhance your touchesBegan implementation a little for further debugging:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"HELLO FROM INSIDE");
NSLog(#"our delegate is set towards: %#", delegate);
...
}
Does it log something useful in that second logging statement?
I presume it prints nil and that would be the root cause of your issue; you forgot to assign the delegate.
I have two classes:
A UIViewController and a class that's subclassing NSObject that acts as a downloading helper class called OfficesParser. OfficesParser is using ASIHTTPRequest and I set the delegate for the download requests to be my UIViewController.
EDIT: Interface for the UIViewController:
#interface OfficesViewController : UIViewController <UITableViewDelegate, UITableViewDataSource, ASIHTTPRequestDelegate> {
OfficesParser *officesParser;
}
#property (nonatomic, retain) OfficesParser *officesParser;
#end
In the UIViewController implementation I set up the OfficesParser like so:
- (void)viewDidLoad {
[super viewDidLoad];
self.officesParser = [[[OfficesParser alloc] init] autorelease]; // self.officesParser is retained
}
Then before the view appears I call my my OfficesParser object to download some data for me:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.officesParser doNetworkOperations];
}
Also in my UIViewController I have setup the appropriate delegate methods to deal with the data after it has been downloaded. In particular I'm interested in this delegate method that will run after all the data has been processed in my download queue. I can see that the delegate method is running from the log. But for some reason self.officesParser in here is nil.
- (void)queueFinished:(ASINetworkQueue *)queue {
DLog(#"queueFinished running");
[self.officesParser test]; // test will not get called because self.officesParser is nil
}